Merge branch 'feature' of https://git.valuefrontier.cn/vf/vf_react into feature

This commit is contained in:
2025-10-28 11:21:11 +08:00
5 changed files with 112 additions and 115 deletions

View File

@@ -1,14 +1,14 @@
import React from 'react'; import React from 'react';
import { // import {
Box, // Box,
Alert, // Alert,
AlertIcon, // AlertIcon,
AlertTitle, // AlertTitle,
AlertDescription, // AlertDescription,
Button, // Button,
VStack, // VStack,
Container // Container
} from '@chakra-ui/react'; // } from '@chakra-ui/react';
import { logger } from '../utils/logger'; import { logger } from '../utils/logger';
class ErrorBoundary extends React.Component { class ErrorBoundary extends React.Component {
@@ -40,66 +40,68 @@ class ErrorBoundary extends React.Component {
} }
render() { render() {
// 如果有错误,显示错误边界(所有环境 // 静默模式:捕获错误并记录日志(已在 componentDidCatch 中完成
if (this.state.hasError) { // 但继续渲染子组件,不显示错误页面
return ( // 注意:如果组件因错误无法渲染,该区域可能显示为空白
<Container maxW="lg" py={20}> // // 如果有错误,显示错误边界(所有环境)
<VStack spacing={6}> // if (this.state.hasError) {
<Alert status="error" borderRadius="lg" p={6}> // return (
<AlertIcon boxSize="24px" /> // <Container maxW="lg" py={20}>
<Box> // <VStack spacing={6}>
<AlertTitle fontSize="lg" mb={2}> // <Alert status="error" borderRadius="lg" p={6}>
页面出现错误 // <AlertIcon boxSize="24px" />
</AlertTitle> // <Box>
<AlertDescription> // <AlertTitle fontSize="lg" mb={2}>
{process.env.NODE_ENV === 'development' // 页面出现错误!
? '组件渲染时发生错误,请查看下方详情和控制台日志。' // </AlertTitle>
: '页面加载时发生了未预期的错误,请尝试刷新页面。'} // <AlertDescription>
</AlertDescription> // {process.env.NODE_ENV === 'development'
</Box> // ? '组件渲染时发生错误,请查看下方详情和控制台日志。'
</Alert> // : '页面加载时发生了未预期的错误,请尝试刷新页面。'}
// </AlertDescription>
// </Box>
// </Alert>
{/* 开发环境显示详细错误信息 */} // {/* 开发环境显示详细错误信息 */}
{process.env.NODE_ENV === 'development' && this.state.error && ( // {process.env.NODE_ENV === 'development' && this.state.error && (
<Box // <Box
w="100%" // w="100%"
bg="red.50" // bg="red.50"
p={4} // p={4}
borderRadius="lg" // borderRadius="lg"
fontSize="sm" // fontSize="sm"
overflow="auto" // overflow="auto"
maxH="400px" // maxH="400px"
border="1px" // border="1px"
borderColor="red.200" // borderColor="red.200"
> // >
<Box fontWeight="bold" mb={2} color="red.700">错误详情:</Box> // <Box fontWeight="bold" mb={2} color="red.700">错误详情:</Box>
<Box as="pre" whiteSpace="pre-wrap" color="red.900" fontSize="xs"> // <Box as="pre" whiteSpace="pre-wrap" color="red.900" fontSize="xs">
<Box fontWeight="bold" mb={1}>{this.state.error.name}: {this.state.error.message}</Box> // <Box fontWeight="bold" mb={1}>{this.state.error.name}: {this.state.error.message}</Box>
{this.state.error.stack && ( // {this.state.error.stack && (
<Box mt={2} color="gray.700">{this.state.error.stack}</Box> // <Box mt={2} color="gray.700">{this.state.error.stack}</Box>
)} // )}
{this.state.errorInfo && this.state.errorInfo.componentStack && ( // {this.state.errorInfo && this.state.errorInfo.componentStack && (
<> // <>
<Box fontWeight="bold" mt={3} mb={1} color="red.700">组件堆栈:</Box> // <Box fontWeight="bold" mt={3} mb={1} color="red.700">组件堆栈:</Box>
<Box color="gray.700">{this.state.errorInfo.componentStack}</Box> // <Box color="gray.700">{this.state.errorInfo.componentStack}</Box>
</> // </>
)} // )}
</Box> // </Box>
</Box> // </Box>
)} // )}
<Button
colorScheme="blue"
size="lg"
onClick={() => window.location.reload()}
>
重新加载页面
</Button>
</VStack>
</Container>
);
}
// <Button
// colorScheme="blue"
// size="lg"
// onClick={() => window.location.reload()}
// >
// 重新加载页面
// </Button>
// </VStack>
// </Container>
// );
// }
return this.props.children; return this.props.children;
} }
} }

View File

@@ -480,9 +480,10 @@ export default function MidjourneyHeroSection() {
minH="100vh" minH="100vh"
bg="linear-gradient(135deg, #0f0f0f 0%, #1a1a1a 50%, #000000 100%)" bg="linear-gradient(135deg, #0f0f0f 0%, #1a1a1a 50%, #000000 100%)"
overflow="hidden" overflow="hidden"
pointerEvents="none"
> >
{/* 粒子背景 */} {/* 粒子背景 */}
<Box position="absolute" inset={0} zIndex={0}> <Box position="absolute" inset={0} zIndex={-1} pointerEvents="none">
<Particles <Particles
id="tsparticles" id="tsparticles"
init={particlesInit} init={particlesInit}
@@ -499,7 +500,7 @@ export default function MidjourneyHeroSection() {
<DataStreams /> <DataStreams />
{/* 内容容器 */} {/* 内容容器 */}
<Container maxW="7xl" position="relative" zIndex={20} pt={20} pb={20}> <Container maxW="7xl" position="relative" zIndex={1} pt={20} pb={20}>
<Grid templateColumns={{ base: '1fr', lg: 'repeat(2, 1fr)' }} gap={12} alignItems="center" minH="70vh"> <Grid templateColumns={{ base: '1fr', lg: 'repeat(2, 1fr)' }} gap={12} alignItems="center" minH="70vh">
{/* 左侧文本内容 */} {/* 左侧文本内容 */}
@@ -776,7 +777,7 @@ export default function MidjourneyHeroSection() {
borderRadius="full" borderRadius="full"
filter="blur(40px)" filter="blur(40px)"
animation="pulse 4s ease-in-out infinite" animation="pulse 4s ease-in-out infinite"
animationDelay="2s" sx={{ animationDelay: '2s' }}
/> />
</Box> </Box>
</Box> </Box>
@@ -793,7 +794,7 @@ export default function MidjourneyHeroSection() {
right={0} right={0}
h="128px" h="128px"
bgGradient="linear(to-t, black, transparent)" bgGradient="linear(to-t, black, transparent)"
zIndex={10} zIndex={-1}
/> />
{/* 全局样式 */} {/* 全局样式 */}

View File

@@ -138,9 +138,9 @@ const PopularKeywords = ({ onKeywordClick, keywords: propKeywords }) => {
</span> </span>
{/* 所有标签 */} {/* 所有标签 */}
{keywords.map((item) => ( {keywords.map((item, index) => (
<Tag <Tag
key={item.concept_id} key={item.concept_id || `keyword-${index}`}
color={getTagColor(item.change_pct)} color={getTagColor(item.change_pct)}
style={{ style={{
cursor: 'pointer', cursor: 'pointer',

View File

@@ -523,7 +523,11 @@ const UnifiedSearchBox = ({
onFocus={onSearchFocus} onFocus={onSearchFocus}
options={stockOptions} options={stockOptions}
placeholder="请输入股票代码/股票名称/相关话题" placeholder="请输入股票代码/股票名称/相关话题"
onPressEnter={handleMainSearch} onKeyDown={(e) => {
if (e.key === 'Enter') {
handleMainSearch();
}
}}
style={{ flex: 1 }} style={{ flex: 1 }}
size="large" size="large"
notFoundContent={inputValue && stockOptions.length === 0 ? "未找到匹配的股票" : null} notFoundContent={inputValue && stockOptions.length === 0 ? "未找到匹配的股票" : null}

View File

@@ -51,11 +51,11 @@ export default function HomePage() {
const coreFeatures = [ const coreFeatures = [
{ {
id: 'news-catalyst', id: 'news-catalyst',
title: '新闻催化分析', title: '新闻中心',
description: '实时新闻事件分析,捕捉市场催化因子', description: '实时新闻事件分析,捕捉市场催化因子',
icon: '📊', icon: '📊',
color: 'yellow', color: 'yellow',
url: 'https://valuefrontier.cn/community', url: '/community',
badge: '核心', badge: '核心',
featured: true featured: true
}, },
@@ -65,7 +65,7 @@ export default function HomePage() {
description: '热门概念与主题投资分析追踪', description: '热门概念与主题投资分析追踪',
icon: '🎯', icon: '🎯',
color: 'purple', color: 'purple',
url: 'https://valuefrontier.cn/concepts', url: '/concepts',
badge: '热门' badge: '热门'
}, },
{ {
@@ -74,7 +74,7 @@ export default function HomePage() {
description: '全面的个股基本面信息整合', description: '全面的个股基本面信息整合',
icon: '📈', icon: '📈',
color: 'blue', color: 'blue',
url: 'https://valuefrontier.cn/stocks', url: '/stocks',
badge: '全面' badge: '全面'
}, },
{ {
@@ -83,7 +83,7 @@ export default function HomePage() {
description: '涨停板数据深度分析与规律挖掘', description: '涨停板数据深度分析与规律挖掘',
icon: '🚀', icon: '🚀',
color: 'green', color: 'green',
url: 'https://valuefrontier.cn/limit-analyse', url: '/limit-analyse',
badge: '精准' badge: '精准'
}, },
{ {
@@ -92,7 +92,7 @@ export default function HomePage() {
description: '个股全方位分析与投资决策支持', description: '个股全方位分析与投资决策支持',
icon: '🧭', icon: '🧭',
color: 'orange', color: 'orange',
url: 'https://valuefrontier.cn/company?scode=688256', url: '/company?scode=688256',
badge: '专业' badge: '专业'
}, },
{ {
@@ -106,15 +106,6 @@ export default function HomePage() {
} }
]; ];
// 个人中心配置
// const personalCenter = {
// title: '个人中心',
// description: '账户管理与个人设置',
// icon: '👤',
// color: 'gray',
// url: 'https://valuefrontier.cn/home/center'
// };
// @TODO 如何区分内部链接和外部链接? // @TODO 如何区分内部链接和外部链接?
const handleProductClick = (url) => { const handleProductClick = (url) => {
if (url.startsWith('http')) { if (url.startsWith('http')) {
@@ -202,7 +193,7 @@ export default function HomePage() {
</> </>
)} )}
<Container maxW="7xl" position="relative" zIndex={2} px={containerPx}> <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: 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 }}> <VStack spacing={{ base: 4, md: 5, lg: 6 }} textAlign="center" pt={{ base: 4, md: 6, lg: 8 }}>
@@ -225,7 +216,7 @@ export default function HomePage() {
<Box pb={{ base: 8, md: 12 }}> <Box pb={{ base: 8, md: 12 }}>
<VStack spacing={{ base: 6, md: 8 }}> <VStack spacing={{ base: 6, md: 8 }}>
{/* 新闻催化分析 - 突出显示 */} {/* 新闻中心 - 突出显示 */}
<Card <Card
bg="transparent" bg="transparent"
border="2px solid" border="2px solid"
@@ -326,29 +317,28 @@ export default function HomePage() {
{/* 其他5个功能 */} {/* 其他5个功能 */}
<SimpleGrid columns={{ base: 1, md: 2, lg: 3 }} spacing={{ base: 4, md: 5, lg: 6 }} w="100%"> <SimpleGrid columns={{ base: 1, md: 2, lg: 3 }} spacing={{ base: 4, md: 5, lg: 6 }} w="100%">
{coreFeatures.slice(1).map((feature) => ( {coreFeatures.slice(1).map((feature) => (
<Card <Card
key={feature.id} key={feature.id}
bg="whiteAlpha.100" bg="whiteAlpha.100"
backdropFilter="blur(10px)" backdropFilter="blur(10px)"
border="1px solid" border="1px solid"
borderColor="whiteAlpha.200" borderColor="whiteAlpha.200"
borderRadius={{ base: 'xl', md: '2xl' }} borderRadius={{ base: 'xl', md: '2xl' }}
cursor="pointer" transition="all 0.3s ease"
transition="all 0.3s ease" _hover={{
_hover={{ bg: 'whiteAlpha.200',
bg: 'whiteAlpha.200', borderColor: `${feature.color}.400`,
borderColor: `${feature.color}.400`, transform: 'translateY(-4px)',
transform: 'translateY(-4px)', shadow: '2xl'
shadow: '2xl' }}
}} _active={{
_active={{ bg: 'whiteAlpha.200',
bg: 'whiteAlpha.200', borderColor: `${feature.color}.400`,
borderColor: `${feature.color}.400`, transform: 'translateY(-2px)'
transform: 'translateY(-2px)' }}
}} onClick={() => handleProductClick(feature.url)}
onClick={() => handleProductClick(feature.url)} minH={{ base: 'auto', md: '180px' }}
minH={{ base: 'auto', md: '180px' }} >
>
<CardBody p={{ base: 5, md: 6 }}> <CardBody p={{ base: 5, md: 6 }}>
<VStack spacing={{ base: 3, md: 4 }} align="start" h="100%"> <VStack spacing={{ base: 3, md: 4 }} align="start" h="100%">
<HStack> <HStack>