feat: homets 化 创建类型定义文件
创建常量配置文件 创建自定义 Hook 创建组件目录 创建 HeroHeader 组件 创建 FeaturedFeatureCard 组件 创建 FeatureCard 组件 创建新的 HomePage.tsx
This commit is contained in:
66
src/constants/homeFeatures.ts
Normal file
66
src/constants/homeFeatures.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
// src/constants/homeFeatures.ts
|
||||||
|
// 首页功能特性配置
|
||||||
|
|
||||||
|
import type { Feature } from '@/types/home';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 核心功能特性列表
|
||||||
|
* 第一个功能为特色功能,会以突出样式显示
|
||||||
|
*/
|
||||||
|
export const CORE_FEATURES: Feature[] = [
|
||||||
|
{
|
||||||
|
id: 'news-catalyst',
|
||||||
|
title: '新闻中心',
|
||||||
|
description: '实时新闻事件分析,捕捉市场催化因子',
|
||||||
|
icon: '📊',
|
||||||
|
color: 'yellow',
|
||||||
|
url: '/community',
|
||||||
|
badge: '核心',
|
||||||
|
featured: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'concepts',
|
||||||
|
title: '概念中心',
|
||||||
|
description: '热门概念与主题投资分析追踪',
|
||||||
|
icon: '🎯',
|
||||||
|
color: 'purple',
|
||||||
|
url: '/concepts',
|
||||||
|
badge: '热门'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'stocks',
|
||||||
|
title: '个股信息汇总',
|
||||||
|
description: '全面的个股基本面信息整合',
|
||||||
|
icon: '📈',
|
||||||
|
color: 'blue',
|
||||||
|
url: '/stocks',
|
||||||
|
badge: '全面'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'limit-analyse',
|
||||||
|
title: '涨停板块分析',
|
||||||
|
description: '涨停板数据深度分析与规律挖掘',
|
||||||
|
icon: '🚀',
|
||||||
|
color: 'green',
|
||||||
|
url: '/limit-analyse',
|
||||||
|
badge: '精准'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'company',
|
||||||
|
title: '个股罗盘',
|
||||||
|
description: '个股全方位分析与投资决策支持',
|
||||||
|
icon: '🧭',
|
||||||
|
color: 'orange',
|
||||||
|
url: '/company?scode=688256',
|
||||||
|
badge: '专业'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'trading-simulation',
|
||||||
|
title: '模拟盘交易',
|
||||||
|
description: '100万起始资金,体验真实交易环境',
|
||||||
|
icon: '💰',
|
||||||
|
color: 'teal',
|
||||||
|
url: '/trading-simulation',
|
||||||
|
badge: '实战'
|
||||||
|
}
|
||||||
|
];
|
||||||
57
src/hooks/useHomeResponsive.ts
Normal file
57
src/hooks/useHomeResponsive.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
// src/hooks/useHomeResponsive.ts
|
||||||
|
// 首页响应式配置 Hook
|
||||||
|
|
||||||
|
import { useBreakpointValue } from '@chakra-ui/react';
|
||||||
|
import type { ResponsiveConfig } from '@/types/home';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 首页响应式配置 Hook
|
||||||
|
* 集中管理所有响应式断点值
|
||||||
|
*
|
||||||
|
* @returns 响应式配置对象
|
||||||
|
*/
|
||||||
|
export const useHomeResponsive = (): ResponsiveConfig => {
|
||||||
|
const heroHeight = useBreakpointValue({
|
||||||
|
base: '60vh',
|
||||||
|
md: '80vh',
|
||||||
|
lg: '100vh'
|
||||||
|
});
|
||||||
|
|
||||||
|
const headingSize = useBreakpointValue({
|
||||||
|
base: 'xl',
|
||||||
|
md: '3xl',
|
||||||
|
lg: '4xl'
|
||||||
|
});
|
||||||
|
|
||||||
|
const headingLetterSpacing = useBreakpointValue({
|
||||||
|
base: '-1px',
|
||||||
|
md: '-1.5px',
|
||||||
|
lg: '-2px'
|
||||||
|
});
|
||||||
|
|
||||||
|
const heroTextSize = useBreakpointValue({
|
||||||
|
base: 'md',
|
||||||
|
md: 'lg',
|
||||||
|
lg: 'xl'
|
||||||
|
});
|
||||||
|
|
||||||
|
const containerPx = useBreakpointValue({
|
||||||
|
base: 10,
|
||||||
|
md: 10,
|
||||||
|
lg: 10
|
||||||
|
});
|
||||||
|
|
||||||
|
const showDecorations = useBreakpointValue({
|
||||||
|
base: false,
|
||||||
|
md: true
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
heroHeight,
|
||||||
|
headingSize,
|
||||||
|
headingLetterSpacing,
|
||||||
|
heroTextSize,
|
||||||
|
containerPx,
|
||||||
|
showDecorations
|
||||||
|
};
|
||||||
|
};
|
||||||
33
src/types/home.ts
Normal file
33
src/types/home.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// src/types/home.ts
|
||||||
|
// HomePage 相关类型定义
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 功能特性配置
|
||||||
|
*/
|
||||||
|
export interface Feature {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
icon: string;
|
||||||
|
color: string;
|
||||||
|
url: string;
|
||||||
|
badge: string;
|
||||||
|
featured?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应式配置
|
||||||
|
*/
|
||||||
|
export interface ResponsiveConfig {
|
||||||
|
heroHeight: string | undefined;
|
||||||
|
headingSize: string | undefined;
|
||||||
|
headingLetterSpacing: string | undefined;
|
||||||
|
heroTextSize: string | undefined;
|
||||||
|
containerPx: number | undefined;
|
||||||
|
showDecorations: boolean | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 功能卡片点击处理函数类型
|
||||||
|
*/
|
||||||
|
export type FeatureClickHandler = (feature: Feature) => void;
|
||||||
@@ -1,379 +0,0 @@
|
|||||||
// src/views/Home/HomePage.js - 专业投资分析平台
|
|
||||||
import React, { useEffect, useCallback } from 'react';
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
Container,
|
|
||||||
Heading,
|
|
||||||
Text,
|
|
||||||
Card,
|
|
||||||
CardBody,
|
|
||||||
Badge,
|
|
||||||
Button,
|
|
||||||
Flex,
|
|
||||||
VStack,
|
|
||||||
HStack,
|
|
||||||
SimpleGrid,
|
|
||||||
useBreakpointValue
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { useAuth } from '../../contexts/AuthContext';
|
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import heroBg from '../../assets/img/BackgroundCard1.png';
|
|
||||||
import '../../styles/home-animations.css';
|
|
||||||
import { logger } from '../../utils/logger';
|
|
||||||
import { usePostHogTrack } from '../../hooks/usePostHogRedux';
|
|
||||||
import { ACQUISITION_EVENTS } from '../../lib/constants';
|
|
||||||
|
|
||||||
export default function HomePage() {
|
|
||||||
const { user, isAuthenticated } = useAuth(); // ⚡ 移除 isLoading,不再依赖它
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const { track } = usePostHogTrack(); // PostHog 追踪
|
|
||||||
const [imageLoaded, setImageLoaded] = React.useState(false);
|
|
||||||
|
|
||||||
// 响应式配置
|
|
||||||
const heroHeight = useBreakpointValue({ base: '60vh', md: '80vh', lg: '100vh' });
|
|
||||||
const headingSize = useBreakpointValue({ base: 'xl', md: '3xl', lg: '4xl' });
|
|
||||||
const headingLetterSpacing = useBreakpointValue({ base: '-1px', md: '-1.5px', lg: '-2px' });
|
|
||||||
const heroTextSize = useBreakpointValue({ base: 'md', md: 'lg', lg: 'xl' });
|
|
||||||
const containerPx = useBreakpointValue({ base: 10, md: 10, lg: 10 });
|
|
||||||
const showDecorations = useBreakpointValue({ base: false, md: true });
|
|
||||||
|
|
||||||
// 保留原有的调试信息
|
|
||||||
useEffect(() => {
|
|
||||||
logger.debug('HomePage', 'AuthContext状态', {
|
|
||||||
userId: user?.id,
|
|
||||||
username: user?.username,
|
|
||||||
nickname: user?.nickname,
|
|
||||||
isAuthenticated,
|
|
||||||
hasUser: !!user
|
|
||||||
});
|
|
||||||
}, [user?.id, isAuthenticated]); // 只依赖 user.id,避免无限循环
|
|
||||||
|
|
||||||
// 🎯 PostHog 追踪:页面浏览
|
|
||||||
useEffect(() => {
|
|
||||||
track(ACQUISITION_EVENTS.LANDING_PAGE_VIEWED, {
|
|
||||||
timestamp: new Date().toISOString(),
|
|
||||||
is_authenticated: isAuthenticated,
|
|
||||||
user_id: user?.id || null,
|
|
||||||
});
|
|
||||||
}, [track, isAuthenticated, user?.id]);
|
|
||||||
|
|
||||||
// 核心功能配置 - 5个主要功能
|
|
||||||
const coreFeatures = [
|
|
||||||
{
|
|
||||||
id: 'news-catalyst',
|
|
||||||
title: '新闻中心',
|
|
||||||
description: '实时新闻事件分析,捕捉市场催化因子',
|
|
||||||
icon: '📊',
|
|
||||||
color: 'yellow',
|
|
||||||
url: '/community',
|
|
||||||
badge: '核心',
|
|
||||||
featured: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'concepts',
|
|
||||||
title: '概念中心',
|
|
||||||
description: '热门概念与主题投资分析追踪',
|
|
||||||
icon: '🎯',
|
|
||||||
color: 'purple',
|
|
||||||
url: '/concepts',
|
|
||||||
badge: '热门'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'stocks',
|
|
||||||
title: '个股信息汇总',
|
|
||||||
description: '全面的个股基本面信息整合',
|
|
||||||
icon: '📈',
|
|
||||||
color: 'blue',
|
|
||||||
url: '/stocks',
|
|
||||||
badge: '全面'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'limit-analyse',
|
|
||||||
title: '涨停板块分析',
|
|
||||||
description: '涨停板数据深度分析与规律挖掘',
|
|
||||||
icon: '🚀',
|
|
||||||
color: 'green',
|
|
||||||
url: '/limit-analyse',
|
|
||||||
badge: '精准'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'company',
|
|
||||||
title: '个股罗盘',
|
|
||||||
description: '个股全方位分析与投资决策支持',
|
|
||||||
icon: '🧭',
|
|
||||||
color: 'orange',
|
|
||||||
url: '/company?scode=688256',
|
|
||||||
badge: '专业'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'trading-simulation',
|
|
||||||
title: '模拟盘交易',
|
|
||||||
description: '100万起始资金,体验真实交易环境',
|
|
||||||
icon: '💰',
|
|
||||||
color: 'teal',
|
|
||||||
url: '/trading-simulation',
|
|
||||||
badge: '实战'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
// @TODO 如何区分内部链接和外部链接?
|
|
||||||
const handleProductClick = useCallback((feature) => {
|
|
||||||
// 🎯 PostHog 追踪:功能卡片点击
|
|
||||||
track(ACQUISITION_EVENTS.FEATURE_CARD_CLICKED, {
|
|
||||||
feature_id: feature.id,
|
|
||||||
feature_title: feature.title,
|
|
||||||
feature_url: feature.url,
|
|
||||||
is_featured: feature.featured || false,
|
|
||||||
link_type: feature.url.startsWith('http') ? 'external' : 'internal',
|
|
||||||
});
|
|
||||||
|
|
||||||
// 原有导航逻辑
|
|
||||||
if (feature.url.startsWith('http')) {
|
|
||||||
// 外部链接,直接打开
|
|
||||||
window.open(feature.url, '_blank');
|
|
||||||
} else {
|
|
||||||
// 内部路由
|
|
||||||
navigate(feature.url);
|
|
||||||
}
|
|
||||||
}, [track, navigate]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box>
|
|
||||||
|
|
||||||
{/* Hero Section - 深色科技风格 */}
|
|
||||||
<Box
|
|
||||||
position="relative"
|
|
||||||
minH={heroHeight}
|
|
||||||
bg="linear-gradient(135deg, #0E0C15 0%, #15131D 50%, #252134 100%)"
|
|
||||||
overflow="hidden"
|
|
||||||
>
|
|
||||||
{/* 背景图片和装饰 - 优化:延迟加载 */}
|
|
||||||
<Box
|
|
||||||
position="absolute"
|
|
||||||
top="0"
|
|
||||||
right="0"
|
|
||||||
w="50%"
|
|
||||||
h="100%"
|
|
||||||
bgImage={imageLoaded ? `url(${heroBg})` : 'none'}
|
|
||||||
bgSize="cover"
|
|
||||||
bgPosition="center"
|
|
||||||
opacity={imageLoaded ? 0.3 : 0}
|
|
||||||
transition="opacity 0.5s ease-in"
|
|
||||||
_after={{
|
|
||||||
content: '""',
|
|
||||||
position: 'absolute',
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
bottom: 0,
|
|
||||||
background: 'linear-gradient(90deg, rgba(14, 12, 21, 0.9) 0%, rgba(14, 12, 21, 0.3) 100%)'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{/* 预加载背景图片 */}
|
|
||||||
<Box display="none">
|
|
||||||
<img
|
|
||||||
src={heroBg}
|
|
||||||
alt=""
|
|
||||||
onLoad={() => setImageLoaded(true)}
|
|
||||||
onError={() => setImageLoaded(true)}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* 装饰性几何图形 - 移动端隐藏 */}
|
|
||||||
{showDecorations && (
|
|
||||||
<>
|
|
||||||
<Box
|
|
||||||
position="absolute"
|
|
||||||
top="20%"
|
|
||||||
left="10%"
|
|
||||||
w={{ base: '100px', md: '150px', lg: '200px' }}
|
|
||||||
h={{ base: '100px', md: '150px', lg: '200px' }}
|
|
||||||
borderRadius="50%"
|
|
||||||
bg="rgba(255, 215, 0, 0.1)"
|
|
||||||
filter="blur(80px)"
|
|
||||||
className="float-animation"
|
|
||||||
/>
|
|
||||||
<Box
|
|
||||||
position="absolute"
|
|
||||||
bottom="30%"
|
|
||||||
right="20%"
|
|
||||||
w={{ base: '80px', md: '120px', lg: '150px' }}
|
|
||||||
h={{ base: '80px', md: '120px', lg: '150px' }}
|
|
||||||
borderRadius="50%"
|
|
||||||
bg="rgba(138, 43, 226, 0.1)"
|
|
||||||
filter="blur(60px)"
|
|
||||||
className="float-animation-reverse"
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Container maxW="7xl" position="relative" zIndex={30} px={containerPx}>
|
|
||||||
<VStack spacing={{ base: 8, md: 12, lg: 16 }} align="stretch" minH={heroHeight} justify="center">
|
|
||||||
{/* 主标题区域 */}
|
|
||||||
<VStack spacing={{ base: 4, md: 5, lg: 6 }} textAlign="center" pt={{ base: 4, md: 6, lg: 8 }}>
|
|
||||||
<Heading
|
|
||||||
size={headingSize}
|
|
||||||
color="white"
|
|
||||||
fontWeight="900"
|
|
||||||
letterSpacing={headingLetterSpacing}
|
|
||||||
lineHeight="shorter"
|
|
||||||
>
|
|
||||||
智能投资分析平台
|
|
||||||
</Heading>
|
|
||||||
<Text fontSize={heroTextSize} color="whiteAlpha.800" maxW={{ base: '100%', md: '2xl', lg: '3xl' }} lineHeight="tall" px={{ base: 4, md: 0 }}>
|
|
||||||
专业投资研究工具,助您把握市场机遇
|
|
||||||
</Text>
|
|
||||||
</VStack>
|
|
||||||
|
|
||||||
|
|
||||||
{/* 核心功能面板 */}
|
|
||||||
<Box pb={{ base: 8, md: 12 }}>
|
|
||||||
<VStack spacing={{ base: 6, md: 8 }}>
|
|
||||||
|
|
||||||
{/* 新闻中心 - 突出显示 */}
|
|
||||||
<Card
|
|
||||||
bg="transparent"
|
|
||||||
border="2px solid"
|
|
||||||
borderColor="yellow.400"
|
|
||||||
borderRadius={{ base: '2xl', md: '3xl' }}
|
|
||||||
overflow="hidden"
|
|
||||||
position="relative"
|
|
||||||
shadow="2xl"
|
|
||||||
w="100%"
|
|
||||||
_before={{
|
|
||||||
content: '""',
|
|
||||||
position: 'absolute',
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
bottom: 0,
|
|
||||||
background: 'linear-gradient(135deg, rgba(255, 215, 0, 0.1) 0%, rgba(255, 165, 0, 0.05) 100%)',
|
|
||||||
zIndex: 0
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CardBody p={{ base: 6, md: 8 }} position="relative" zIndex={1}>
|
|
||||||
{/* 响应式布局:移动端纵向,桌面端横向 */}
|
|
||||||
<Flex
|
|
||||||
direction={{ base: 'column', md: 'row' }}
|
|
||||||
align={{ base: 'stretch', md: 'center' }}
|
|
||||||
justify={{ base: 'flex-start', md: 'space-between' }}
|
|
||||||
gap={{ base: 4, md: 6 }}
|
|
||||||
>
|
|
||||||
<Flex align="center" gap={{ base: 4, md: 6 }} flex={1}>
|
|
||||||
<Box
|
|
||||||
p={{ base: 3, md: 4 }}
|
|
||||||
borderRadius={{ base: 'lg', md: 'xl' }}
|
|
||||||
bg="yellow.400"
|
|
||||||
color="black"
|
|
||||||
>
|
|
||||||
<Text fontSize={{ base: '2xl', md: '3xl' }}>{coreFeatures[0].icon}</Text>
|
|
||||||
</Box>
|
|
||||||
<VStack align="start" spacing={{ base: 1, md: 2 }} flex={1}>
|
|
||||||
<HStack>
|
|
||||||
<Heading size={{ base: 'lg', md: 'xl' }} color="white">
|
|
||||||
{coreFeatures[0].title}
|
|
||||||
</Heading>
|
|
||||||
<Badge colorScheme="yellow" variant="solid" fontSize={{ base: 'xs', md: 'sm' }}>
|
|
||||||
{coreFeatures[0].badge}
|
|
||||||
</Badge>
|
|
||||||
</HStack>
|
|
||||||
<Text color="whiteAlpha.800" fontSize={{ base: 'md', md: 'lg' }} maxW={{ md: 'md' }} lineHeight="tall">
|
|
||||||
{coreFeatures[0].description}
|
|
||||||
</Text>
|
|
||||||
</VStack>
|
|
||||||
</Flex>
|
|
||||||
<Button
|
|
||||||
colorScheme="yellow"
|
|
||||||
size={{ base: 'md', md: 'lg' }}
|
|
||||||
borderRadius="full"
|
|
||||||
fontWeight="bold"
|
|
||||||
w={{ base: '100%', md: 'auto' }}
|
|
||||||
onClick={() => handleProductClick(coreFeatures[0])}
|
|
||||||
minH="44px"
|
|
||||||
flexShrink={0}
|
|
||||||
>
|
|
||||||
进入功能 →
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* 其他5个功能 */}
|
|
||||||
<SimpleGrid columns={{ base: 1, md: 2, lg: 3 }} spacing={{ base: 4, md: 5, lg: 6 }} w="100%">
|
|
||||||
{coreFeatures.slice(1).map((feature) => (
|
|
||||||
<Card
|
|
||||||
key={feature.id}
|
|
||||||
bg="whiteAlpha.100"
|
|
||||||
backdropFilter="blur(10px)"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor="whiteAlpha.200"
|
|
||||||
borderRadius={{ base: 'xl', md: '2xl' }}
|
|
||||||
transition="all 0.3s ease"
|
|
||||||
_hover={{
|
|
||||||
bg: 'whiteAlpha.200',
|
|
||||||
borderColor: `${feature.color}.400`,
|
|
||||||
transform: 'translateY(-4px)',
|
|
||||||
shadow: '2xl'
|
|
||||||
}}
|
|
||||||
_active={{
|
|
||||||
bg: 'whiteAlpha.200',
|
|
||||||
borderColor: `${feature.color}.400`,
|
|
||||||
transform: 'translateY(-2px)'
|
|
||||||
}}
|
|
||||||
onClick={() => handleProductClick(feature)}
|
|
||||||
minH={{ base: 'auto', md: '180px' }}
|
|
||||||
>
|
|
||||||
<CardBody p={{ base: 5, md: 6 }}>
|
|
||||||
<VStack spacing={{ base: 3, md: 4 }} align="start" h="100%">
|
|
||||||
<HStack>
|
|
||||||
<Box
|
|
||||||
p={{ base: 2, md: 3 }}
|
|
||||||
borderRadius="lg"
|
|
||||||
bg={`${feature.color}.50`}
|
|
||||||
border="1px solid"
|
|
||||||
borderColor={`${feature.color}.200`}
|
|
||||||
>
|
|
||||||
<Text fontSize={{ base: 'xl', md: '2xl' }}>{feature.icon}</Text>
|
|
||||||
</Box>
|
|
||||||
<Badge colorScheme={feature.color} variant="solid" fontSize={{ base: 'xs', md: 'sm' }}>
|
|
||||||
{feature.badge}
|
|
||||||
</Badge>
|
|
||||||
</HStack>
|
|
||||||
|
|
||||||
<VStack align="start" spacing={{ base: 1, md: 2 }} flex={1}>
|
|
||||||
<Heading size={{ base: 'md', md: 'lg' }} color="white">
|
|
||||||
{feature.title}
|
|
||||||
</Heading>
|
|
||||||
<Text color="whiteAlpha.800" fontSize={{ base: 'xs', md: 'sm' }} lineHeight="tall">
|
|
||||||
{feature.description}
|
|
||||||
</Text>
|
|
||||||
</VStack>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
colorScheme={feature.color}
|
|
||||||
size={{ base: 'md', md: 'sm' }}
|
|
||||||
variant="outline"
|
|
||||||
alignSelf="flex-end"
|
|
||||||
w={{ base: '100%', md: 'auto' }}
|
|
||||||
minH="44px"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
handleProductClick(feature);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
使用
|
|
||||||
</Button>
|
|
||||||
</VStack>
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
))}
|
|
||||||
</SimpleGrid>
|
|
||||||
</VStack>
|
|
||||||
</Box>
|
|
||||||
</VStack>
|
|
||||||
</Container>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
139
src/views/Home/HomePage.tsx
Normal file
139
src/views/Home/HomePage.tsx
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
// src/views/Home/HomePage.tsx
|
||||||
|
// 首页 - 专业投资分析平台
|
||||||
|
|
||||||
|
import React, { useEffect, useCallback, useState } from 'react';
|
||||||
|
import { Box, Container, VStack, SimpleGrid } from '@chakra-ui/react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { useAuth } from '@/contexts/AuthContext';
|
||||||
|
import { usePostHogTrack } from '@/hooks/usePostHogRedux';
|
||||||
|
import { useHomeResponsive } from '@/hooks/useHomeResponsive';
|
||||||
|
import { ACQUISITION_EVENTS } from '@/lib/constants';
|
||||||
|
import { CORE_FEATURES } from '@/constants/homeFeatures';
|
||||||
|
import type { Feature } from '@/types/home';
|
||||||
|
import { HeroBackground } from './components/HeroBackground';
|
||||||
|
import { HeroHeader } from './components/HeroHeader';
|
||||||
|
import { FeaturedFeatureCard } from './components/FeaturedFeatureCard';
|
||||||
|
import { FeatureCard } from './components/FeatureCard';
|
||||||
|
import '@/styles/home-animations.css';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 首页组件
|
||||||
|
* 展示平台核心功能,引导用户探索各个功能模块
|
||||||
|
*/
|
||||||
|
const HomePage: React.FC = () => {
|
||||||
|
const { user, isAuthenticated } = useAuth();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { track } = usePostHogTrack();
|
||||||
|
const [imageLoaded, setImageLoaded] = useState(false);
|
||||||
|
|
||||||
|
// 响应式配置
|
||||||
|
const {
|
||||||
|
heroHeight,
|
||||||
|
headingSize,
|
||||||
|
headingLetterSpacing,
|
||||||
|
heroTextSize,
|
||||||
|
containerPx,
|
||||||
|
showDecorations
|
||||||
|
} = useHomeResponsive();
|
||||||
|
|
||||||
|
// PostHog 追踪:页面浏览
|
||||||
|
useEffect(() => {
|
||||||
|
track(ACQUISITION_EVENTS.LANDING_PAGE_VIEWED, {
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
is_authenticated: isAuthenticated,
|
||||||
|
user_id: user?.id || null,
|
||||||
|
});
|
||||||
|
}, [track, isAuthenticated, user?.id]);
|
||||||
|
|
||||||
|
// 功能卡片点击处理
|
||||||
|
const handleFeatureClick = useCallback((feature: Feature) => {
|
||||||
|
// PostHog 追踪:功能卡片点击
|
||||||
|
track(ACQUISITION_EVENTS.FEATURE_CARD_VIEWED, {
|
||||||
|
feature_id: feature.id,
|
||||||
|
feature_title: feature.title,
|
||||||
|
feature_url: feature.url,
|
||||||
|
is_featured: feature.featured || false,
|
||||||
|
link_type: feature.url.startsWith('http') ? 'external' : 'internal',
|
||||||
|
});
|
||||||
|
|
||||||
|
// 导航处理
|
||||||
|
if (feature.url.startsWith('http')) {
|
||||||
|
window.open(feature.url, '_blank');
|
||||||
|
} else {
|
||||||
|
navigate(feature.url);
|
||||||
|
}
|
||||||
|
}, [track, navigate]);
|
||||||
|
|
||||||
|
// 背景图片加载完成回调
|
||||||
|
const handleImageLoad = useCallback(() => {
|
||||||
|
setImageLoaded(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// 特色功能(第一个)
|
||||||
|
const featuredFeature = CORE_FEATURES[0];
|
||||||
|
// 其他功能
|
||||||
|
const regularFeatures = CORE_FEATURES.slice(1);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
{/* Hero Section - 深色科技风格 */}
|
||||||
|
<Box
|
||||||
|
position="relative"
|
||||||
|
minH={heroHeight}
|
||||||
|
bg="linear-gradient(135deg, #0E0C15 0%, #15131D 50%, #252134 100%)"
|
||||||
|
overflow="hidden"
|
||||||
|
>
|
||||||
|
{/* 背景装饰 */}
|
||||||
|
<HeroBackground
|
||||||
|
imageLoaded={imageLoaded}
|
||||||
|
onImageLoad={handleImageLoad}
|
||||||
|
showDecorations={showDecorations}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Container maxW="7xl" position="relative" zIndex={30} px={containerPx}>
|
||||||
|
<VStack
|
||||||
|
spacing={{ base: 8, md: 12, lg: 16 }}
|
||||||
|
align="stretch"
|
||||||
|
minH={heroHeight}
|
||||||
|
justify="center"
|
||||||
|
>
|
||||||
|
{/* 主标题区域 */}
|
||||||
|
<HeroHeader
|
||||||
|
headingSize={headingSize}
|
||||||
|
headingLetterSpacing={headingLetterSpacing}
|
||||||
|
heroTextSize={heroTextSize}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 核心功能面板 */}
|
||||||
|
<Box pb={{ base: 8, md: 12 }}>
|
||||||
|
<VStack spacing={{ base: 6, md: 8 }}>
|
||||||
|
{/* 特色功能卡片 - 新闻中心 */}
|
||||||
|
<FeaturedFeatureCard
|
||||||
|
feature={featuredFeature}
|
||||||
|
onClick={handleFeatureClick}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 其他功能卡片 */}
|
||||||
|
<SimpleGrid
|
||||||
|
columns={{ base: 1, md: 2, lg: 3 }}
|
||||||
|
spacing={{ base: 4, md: 5, lg: 6 }}
|
||||||
|
w="100%"
|
||||||
|
>
|
||||||
|
{regularFeatures.map((feature) => (
|
||||||
|
<FeatureCard
|
||||||
|
key={feature.id}
|
||||||
|
feature={feature}
|
||||||
|
onClick={handleFeatureClick}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</SimpleGrid>
|
||||||
|
</VStack>
|
||||||
|
</Box>
|
||||||
|
</VStack>
|
||||||
|
</Container>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default HomePage;
|
||||||
106
src/views/Home/components/FeatureCard.tsx
Normal file
106
src/views/Home/components/FeatureCard.tsx
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
// src/views/Home/components/FeatureCard.tsx
|
||||||
|
// 普通功能卡片组件
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardBody,
|
||||||
|
VStack,
|
||||||
|
HStack,
|
||||||
|
Box,
|
||||||
|
Heading,
|
||||||
|
Text,
|
||||||
|
Badge,
|
||||||
|
Button
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import type { Feature, FeatureClickHandler } from '@/types/home';
|
||||||
|
|
||||||
|
interface FeatureCardProps {
|
||||||
|
feature: Feature;
|
||||||
|
onClick: FeatureClickHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 普通功能卡片组件
|
||||||
|
* 用于展示除特色功能外的其他功能
|
||||||
|
*/
|
||||||
|
export const FeatureCard: React.FC<FeatureCardProps> = ({
|
||||||
|
feature,
|
||||||
|
onClick
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
bg="whiteAlpha.100"
|
||||||
|
backdropFilter="blur(10px)"
|
||||||
|
border="1px solid"
|
||||||
|
borderColor="whiteAlpha.200"
|
||||||
|
borderRadius={{ base: 'xl', md: '2xl' }}
|
||||||
|
transition="all 0.3s ease"
|
||||||
|
_hover={{
|
||||||
|
bg: 'whiteAlpha.200',
|
||||||
|
borderColor: `${feature.color}.400`,
|
||||||
|
transform: 'translateY(-4px)',
|
||||||
|
shadow: '2xl'
|
||||||
|
}}
|
||||||
|
_active={{
|
||||||
|
bg: 'whiteAlpha.200',
|
||||||
|
borderColor: `${feature.color}.400`,
|
||||||
|
transform: 'translateY(-2px)'
|
||||||
|
}}
|
||||||
|
onClick={() => onClick(feature)}
|
||||||
|
minH={{ base: 'auto', md: '180px' }}
|
||||||
|
cursor="pointer"
|
||||||
|
>
|
||||||
|
<CardBody p={{ base: 5, md: 6 }}>
|
||||||
|
<VStack spacing={{ base: 3, md: 4 }} align="start" h="100%">
|
||||||
|
<HStack>
|
||||||
|
<Box
|
||||||
|
p={{ base: 2, md: 3 }}
|
||||||
|
borderRadius="lg"
|
||||||
|
bg={`${feature.color}.50`}
|
||||||
|
border="1px solid"
|
||||||
|
borderColor={`${feature.color}.200`}
|
||||||
|
>
|
||||||
|
<Text fontSize={{ base: 'xl', md: '2xl' }}>{feature.icon}</Text>
|
||||||
|
</Box>
|
||||||
|
<Badge
|
||||||
|
colorScheme={feature.color}
|
||||||
|
variant="solid"
|
||||||
|
fontSize={{ base: 'xs', md: 'sm' }}
|
||||||
|
>
|
||||||
|
{feature.badge}
|
||||||
|
</Badge>
|
||||||
|
</HStack>
|
||||||
|
|
||||||
|
<VStack align="start" spacing={{ base: 1, md: 2 }} flex={1}>
|
||||||
|
<Heading size={{ base: 'md', md: 'lg' }} color="white">
|
||||||
|
{feature.title}
|
||||||
|
</Heading>
|
||||||
|
<Text
|
||||||
|
color="whiteAlpha.800"
|
||||||
|
fontSize={{ base: 'xs', md: 'sm' }}
|
||||||
|
lineHeight="tall"
|
||||||
|
>
|
||||||
|
{feature.description}
|
||||||
|
</Text>
|
||||||
|
</VStack>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
colorScheme={feature.color}
|
||||||
|
size={{ base: 'md', md: 'sm' }}
|
||||||
|
variant="outline"
|
||||||
|
alignSelf="flex-end"
|
||||||
|
w={{ base: '100%', md: 'auto' }}
|
||||||
|
minH="44px"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onClick(feature);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
使用
|
||||||
|
</Button>
|
||||||
|
</VStack>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
104
src/views/Home/components/FeaturedFeatureCard.tsx
Normal file
104
src/views/Home/components/FeaturedFeatureCard.tsx
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
// src/views/Home/components/FeaturedFeatureCard.tsx
|
||||||
|
// 特色功能卡片组件(新闻中心)
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardBody,
|
||||||
|
Flex,
|
||||||
|
Box,
|
||||||
|
VStack,
|
||||||
|
HStack,
|
||||||
|
Heading,
|
||||||
|
Text,
|
||||||
|
Badge,
|
||||||
|
Button
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import type { Feature, FeatureClickHandler } from '@/types/home';
|
||||||
|
|
||||||
|
interface FeaturedFeatureCardProps {
|
||||||
|
feature: Feature;
|
||||||
|
onClick: FeatureClickHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 特色功能卡片组件
|
||||||
|
* 用于突出显示最重要的功能(如新闻中心)
|
||||||
|
*/
|
||||||
|
export const FeaturedFeatureCard: React.FC<FeaturedFeatureCardProps> = ({
|
||||||
|
feature,
|
||||||
|
onClick
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
bg="transparent"
|
||||||
|
border="2px solid"
|
||||||
|
borderColor="yellow.400"
|
||||||
|
borderRadius={{ base: '2xl', md: '3xl' }}
|
||||||
|
overflow="hidden"
|
||||||
|
position="relative"
|
||||||
|
shadow="2xl"
|
||||||
|
w="100%"
|
||||||
|
_before={{
|
||||||
|
content: '""',
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
background: 'linear-gradient(135deg, rgba(255, 215, 0, 0.1) 0%, rgba(255, 165, 0, 0.05) 100%)',
|
||||||
|
zIndex: 0
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CardBody p={{ base: 6, md: 8 }} position="relative" zIndex={1}>
|
||||||
|
<Flex
|
||||||
|
direction={{ base: 'column', md: 'row' }}
|
||||||
|
align={{ base: 'stretch', md: 'center' }}
|
||||||
|
justify={{ base: 'flex-start', md: 'space-between' }}
|
||||||
|
gap={{ base: 4, md: 6 }}
|
||||||
|
>
|
||||||
|
<Flex align="center" gap={{ base: 4, md: 6 }} flex={1}>
|
||||||
|
<Box
|
||||||
|
p={{ base: 3, md: 4 }}
|
||||||
|
borderRadius={{ base: 'lg', md: 'xl' }}
|
||||||
|
bg="yellow.400"
|
||||||
|
color="black"
|
||||||
|
>
|
||||||
|
<Text fontSize={{ base: '2xl', md: '3xl' }}>{feature.icon}</Text>
|
||||||
|
</Box>
|
||||||
|
<VStack align="start" spacing={{ base: 1, md: 2 }} flex={1}>
|
||||||
|
<HStack>
|
||||||
|
<Heading size={{ base: 'lg', md: 'xl' }} color="white">
|
||||||
|
{feature.title}
|
||||||
|
</Heading>
|
||||||
|
<Badge colorScheme="yellow" variant="solid" fontSize={{ base: 'xs', md: 'sm' }}>
|
||||||
|
{feature.badge}
|
||||||
|
</Badge>
|
||||||
|
</HStack>
|
||||||
|
<Text
|
||||||
|
color="whiteAlpha.800"
|
||||||
|
fontSize={{ base: 'md', md: 'lg' }}
|
||||||
|
maxW={{ md: 'md' }}
|
||||||
|
lineHeight="tall"
|
||||||
|
>
|
||||||
|
{feature.description}
|
||||||
|
</Text>
|
||||||
|
</VStack>
|
||||||
|
</Flex>
|
||||||
|
<Button
|
||||||
|
colorScheme="yellow"
|
||||||
|
size={{ base: 'md', md: 'lg' }}
|
||||||
|
borderRadius="full"
|
||||||
|
fontWeight="bold"
|
||||||
|
w={{ base: '100%', md: 'auto' }}
|
||||||
|
onClick={() => onClick(feature)}
|
||||||
|
minH="44px"
|
||||||
|
flexShrink={0}
|
||||||
|
>
|
||||||
|
进入功能 →
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
87
src/views/Home/components/HeroBackground.tsx
Normal file
87
src/views/Home/components/HeroBackground.tsx
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
// src/views/Home/components/HeroBackground.tsx
|
||||||
|
// 首页英雄区背景装饰组件
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { Box } from '@chakra-ui/react';
|
||||||
|
import heroBg from '@assets/img/BackgroundCard1.png';
|
||||||
|
|
||||||
|
interface HeroBackgroundProps {
|
||||||
|
imageLoaded: boolean;
|
||||||
|
onImageLoad: () => void;
|
||||||
|
showDecorations: boolean | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 首页英雄区背景组件
|
||||||
|
* 包含背景图片和装饰性几何图形
|
||||||
|
*/
|
||||||
|
export const HeroBackground: React.FC<HeroBackgroundProps> = ({
|
||||||
|
imageLoaded,
|
||||||
|
onImageLoad,
|
||||||
|
showDecorations
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* 背景图片 */}
|
||||||
|
<Box
|
||||||
|
position="absolute"
|
||||||
|
top="0"
|
||||||
|
right="0"
|
||||||
|
w="50%"
|
||||||
|
h="100%"
|
||||||
|
bgImage={imageLoaded ? `url(${heroBg})` : 'none'}
|
||||||
|
bgSize="cover"
|
||||||
|
bgPosition="center"
|
||||||
|
opacity={imageLoaded ? 0.3 : 0}
|
||||||
|
transition="opacity 0.5s ease-in"
|
||||||
|
_after={{
|
||||||
|
content: '""',
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
background: 'linear-gradient(90deg, rgba(14, 12, 21, 0.9) 0%, rgba(14, 12, 21, 0.3) 100%)'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 预加载背景图片 */}
|
||||||
|
<Box display="none">
|
||||||
|
<img
|
||||||
|
src={heroBg}
|
||||||
|
alt=""
|
||||||
|
onLoad={onImageLoad}
|
||||||
|
onError={onImageLoad}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* 装饰性几何图形 - 移动端隐藏 */}
|
||||||
|
{showDecorations && (
|
||||||
|
<>
|
||||||
|
<Box
|
||||||
|
position="absolute"
|
||||||
|
top="20%"
|
||||||
|
left="10%"
|
||||||
|
w={{ base: '100px', md: '150px', lg: '200px' }}
|
||||||
|
h={{ base: '100px', md: '150px', lg: '200px' }}
|
||||||
|
borderRadius="50%"
|
||||||
|
bg="rgba(255, 215, 0, 0.1)"
|
||||||
|
filter="blur(80px)"
|
||||||
|
className="float-animation"
|
||||||
|
/>
|
||||||
|
<Box
|
||||||
|
position="absolute"
|
||||||
|
bottom="30%"
|
||||||
|
right="20%"
|
||||||
|
w={{ base: '80px', md: '120px', lg: '150px' }}
|
||||||
|
h={{ base: '80px', md: '120px', lg: '150px' }}
|
||||||
|
borderRadius="50%"
|
||||||
|
bg="rgba(138, 43, 226, 0.1)"
|
||||||
|
filter="blur(60px)"
|
||||||
|
className="float-animation-reverse"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
48
src/views/Home/components/HeroHeader.tsx
Normal file
48
src/views/Home/components/HeroHeader.tsx
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
// src/views/Home/components/HeroHeader.tsx
|
||||||
|
// 首页主标题区域组件
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { Heading, Text, VStack } from '@chakra-ui/react';
|
||||||
|
|
||||||
|
interface HeroHeaderProps {
|
||||||
|
headingSize: string | undefined;
|
||||||
|
headingLetterSpacing: string | undefined;
|
||||||
|
heroTextSize: string | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 首页主标题区域组件
|
||||||
|
* 包含主标题和副标题
|
||||||
|
*/
|
||||||
|
export const HeroHeader: React.FC<HeroHeaderProps> = ({
|
||||||
|
headingSize,
|
||||||
|
headingLetterSpacing,
|
||||||
|
heroTextSize
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<VStack
|
||||||
|
spacing={{ base: 4, md: 5, lg: 6 }}
|
||||||
|
textAlign="center"
|
||||||
|
pt={{ base: 4, md: 6, lg: 8 }}
|
||||||
|
>
|
||||||
|
<Heading
|
||||||
|
size={headingSize}
|
||||||
|
color="white"
|
||||||
|
fontWeight="900"
|
||||||
|
letterSpacing={headingLetterSpacing}
|
||||||
|
lineHeight="shorter"
|
||||||
|
>
|
||||||
|
智能投资分析平台
|
||||||
|
</Heading>
|
||||||
|
<Text
|
||||||
|
fontSize={heroTextSize}
|
||||||
|
color="whiteAlpha.800"
|
||||||
|
maxW={{ base: '100%', md: '2xl', lg: '3xl' }}
|
||||||
|
lineHeight="tall"
|
||||||
|
px={{ base: 4, md: 0 }}
|
||||||
|
>
|
||||||
|
专业投资研究工具,助您把握市场机遇
|
||||||
|
</Text>
|
||||||
|
</VStack>
|
||||||
|
);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user