移除 MidjourneyHeroSection 组件及其所有依赖
1: 删除组件文件 MidjourneyHeroSection.js
2: 修改 HomePage.js
3: 卸载相关 npm 包 @tsparticles/react 和 @tsparticles/slim
This commit is contained in:
@@ -25,8 +25,6 @@
|
||||
"@visx/responsive": "^3.12.0",
|
||||
"@visx/scale": "^3.12.0",
|
||||
"@visx/text": "^3.12.0",
|
||||
"@tsparticles/react": "^3.0.0",
|
||||
"@tsparticles/slim": "^3.0.0",
|
||||
"@visx/visx": "^3.12.0",
|
||||
"@visx/wordcloud": "^3.12.0",
|
||||
"antd": "^5.27.4",
|
||||
@@ -57,6 +55,7 @@
|
||||
"react-github-btn": "^1.2.1",
|
||||
"react-icons": "^4.12.0",
|
||||
"react-input-pin-code": "^1.1.5",
|
||||
"react-is": "^19.0.0",
|
||||
"react-just-parallax": "^3.1.16",
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-redux": "^9.2.0",
|
||||
@@ -64,14 +63,12 @@
|
||||
"react-responsive-masonry": "^2.7.1",
|
||||
"react-router-dom": "^6.30.1",
|
||||
"react-scripts": "^5.0.1",
|
||||
"react-is": "^19.0.0",
|
||||
"react-scroll": "^1.8.4",
|
||||
"react-scroll-into-view": "^2.1.3",
|
||||
"react-table": "^7.7.0",
|
||||
"react-tagsinput": "3.19.0",
|
||||
"react-to-print": "^2.13.0",
|
||||
"react-tsparticles": "^2.12.2",
|
||||
"react-to-print": "^3.0.3",
|
||||
"react-tsparticles": "^2.12.2",
|
||||
"recharts": "^3.1.2",
|
||||
"sass": "^1.49.9",
|
||||
"socket.io-client": "^4.7.4",
|
||||
|
||||
@@ -1,814 +0,0 @@
|
||||
import React, { useRef, useMemo, useState, useEffect } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import Particles from '@tsparticles/react';
|
||||
import { loadSlim } from '@tsparticles/slim';
|
||||
import {
|
||||
Box,
|
||||
Container,
|
||||
Heading,
|
||||
Text,
|
||||
Button,
|
||||
HStack,
|
||||
VStack,
|
||||
Badge,
|
||||
Grid,
|
||||
GridItem,
|
||||
Stat,
|
||||
StatLabel,
|
||||
StatNumber,
|
||||
Flex,
|
||||
Tag,
|
||||
useColorModeValue,
|
||||
} from '@chakra-ui/react';
|
||||
import {
|
||||
LineChart,
|
||||
Line,
|
||||
AreaChart,
|
||||
Area,
|
||||
BarChart,
|
||||
Bar,
|
||||
XAxis,
|
||||
YAxis,
|
||||
CartesianGrid,
|
||||
Tooltip,
|
||||
ResponsiveContainer,
|
||||
ComposedChart,
|
||||
ReferenceLine,
|
||||
ReferenceDot,
|
||||
Cell,
|
||||
} from 'recharts';
|
||||
import { indexService } from '../../../services/eventService';
|
||||
|
||||
// 将后端分钟/分时数据转换为 Recharts 数据
|
||||
const toLineSeries = (resp) => {
|
||||
const arr = resp?.data || [];
|
||||
return arr.map((d, i) => ({ time: d.time || i, value: d.price ?? d.close, volume: d.volume }));
|
||||
};
|
||||
|
||||
// 提取昨日收盘价:优先使用最后一条记录的 prev_close;否则回退到倒数第二条的 close
|
||||
const getPrevClose = (resp) => {
|
||||
const arr = resp?.data || [];
|
||||
if (!arr.length) return null;
|
||||
const last = arr[arr.length - 1] || {};
|
||||
if (last.prev_close !== undefined && last.prev_close !== null && isFinite(Number(last.prev_close))) {
|
||||
return Number(last.prev_close);
|
||||
}
|
||||
const idx = arr.length >= 2 ? arr.length - 2 : arr.length - 1;
|
||||
const k = arr[idx] || {};
|
||||
const candidate = k.close ?? k.c ?? k.price ?? null;
|
||||
return candidate != null ? Number(candidate) : null;
|
||||
};
|
||||
|
||||
// 组合图表组件(折线图 + 成交量柱状图)
|
||||
const CombinedChart = ({ series, title, color = "#FFD700", basePrice = null }) => {
|
||||
const [cursorIndex, setCursorIndex] = useState(0);
|
||||
const cursorRef = useRef(0);
|
||||
|
||||
// 直接将光标设置到最后一个数据点,不再使用动画
|
||||
useEffect(() => {
|
||||
if (!series || series.length === 0) return;
|
||||
// 直接设置到最后一个点
|
||||
const lastIndex = series.length - 1;
|
||||
cursorRef.current = lastIndex;
|
||||
setCursorIndex(lastIndex);
|
||||
}, [series && series.length]);
|
||||
|
||||
|
||||
const yDomain = useMemo(() => {
|
||||
if (!series || series.length === 0) return ['auto', 'auto'];
|
||||
const values = series
|
||||
.map((d) => d?.value)
|
||||
.filter((v) => typeof v === 'number' && isFinite(v));
|
||||
if (values.length === 0) return ['auto', 'auto'];
|
||||
const minVal = Math.min(...values);
|
||||
const maxVal = Math.max(...values);
|
||||
const maxAbs = Math.max(Math.abs(minVal), Math.abs(maxVal));
|
||||
const padding = Math.max(maxAbs * 0.1, 0.2);
|
||||
return [-maxAbs - padding, maxAbs + padding];
|
||||
}, [series]);
|
||||
|
||||
// 当前高亮点
|
||||
const activePoint = useMemo(() => {
|
||||
if (!series || series.length === 0) return null;
|
||||
if (cursorIndex < 0 || cursorIndex >= series.length) return null;
|
||||
return series[cursorIndex];
|
||||
}, [series, cursorIndex]);
|
||||
|
||||
// 稳定的X轴ticks,避免随渲染跳动而闪烁
|
||||
const xTicks = useMemo(() => {
|
||||
if (!series || series.length === 0) return [];
|
||||
const desiredLabels = ['09:30', '10:30', '11:30', '14:00', '15:00'];
|
||||
const set = new Set(series.map(d => d?.time));
|
||||
let ticks = desiredLabels.filter(t => set.has(t));
|
||||
if (ticks.length === 0) {
|
||||
// 回退到首/中/尾的稳定采样,避免空白
|
||||
const len = series.length;
|
||||
const idxs = [0, Math.round(len * 0.25), Math.round(len * 0.5), Math.round(len * 0.75), len - 1];
|
||||
ticks = idxs.map(i => series[i]?.time).filter(Boolean);
|
||||
}
|
||||
return ticks;
|
||||
}, [series && series.length]);
|
||||
|
||||
return (
|
||||
<Box h="full" position="relative">
|
||||
<Text
|
||||
fontSize="xs"
|
||||
color={color}
|
||||
fontFamily="monospace"
|
||||
mb={1}
|
||||
px={2}
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
<ResponsiveContainer width="100%" height="90%">
|
||||
<ComposedChart data={series} margin={{ top: 10, right: 10, left: 0, bottom: 30 }}>
|
||||
<defs>
|
||||
<linearGradient id={`gradient-${title.replace(/[.\s]/g, '')}`} x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stopColor={color} stopOpacity={0.8}/>
|
||||
<stop offset="100%" stopColor={color} stopOpacity={0.2}/>
|
||||
</linearGradient>
|
||||
<linearGradient id={`barGradient-${title.replace(/[.\s]/g, '')}`} x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stopColor={color} stopOpacity={0.3}/>
|
||||
<stop offset="100%" stopColor={color} stopOpacity={0.05}/>
|
||||
</linearGradient>
|
||||
<linearGradient id={`barGradientActive-${title.replace(/[.\s]/g, '')}`} x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stopColor={color} stopOpacity={0.8}/>
|
||||
<stop offset="100%" stopColor={color} stopOpacity={0.3}/>
|
||||
</linearGradient>
|
||||
{/* 发光效果 */}
|
||||
<filter id={`glow-${title.replace(/[.\s]/g, '')}`}>
|
||||
<feGaussianBlur stdDeviation="4" result="coloredBlur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="coloredBlur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="rgba(255, 215, 0, 0.1)" />
|
||||
|
||||
<XAxis
|
||||
dataKey="time"
|
||||
stroke={color}
|
||||
tick={{ fill: color, fontSize: 10 }}
|
||||
tickLine={false}
|
||||
axisLine={{ stroke: `${color}33` }}
|
||||
ticks={xTicks}
|
||||
interval={0}
|
||||
allowDuplicatedCategory={false}
|
||||
/>
|
||||
|
||||
{/* 左Y轴 - 价格 */}
|
||||
<YAxis
|
||||
yAxisId="price"
|
||||
stroke={color}
|
||||
domain={yDomain}
|
||||
tickFormatter={(v) => `${v.toFixed(2)}%`}
|
||||
orientation="left"
|
||||
/>
|
||||
|
||||
{/* 右Y轴 - 成交量(隐藏) */}
|
||||
<YAxis
|
||||
yAxisId="volume"
|
||||
orientation="right"
|
||||
hide
|
||||
domain={[0, 'dataMax + 1000']}
|
||||
/>
|
||||
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
backgroundColor: 'rgba(0,0,0,0.9)',
|
||||
border: `1px solid ${color}`,
|
||||
borderRadius: '8px'
|
||||
}}
|
||||
labelStyle={{ color: '#fff' }}
|
||||
itemStyle={{ color: '#fff' }}
|
||||
labelFormatter={(label) => `时间: ${label}`}
|
||||
formatter={(value, name) => {
|
||||
if (name === 'value') {
|
||||
const pct = Number(value);
|
||||
if (typeof basePrice === 'number' && isFinite(basePrice)) {
|
||||
const price = basePrice * (1 + pct / 100);
|
||||
return [price.toFixed(2), '价格'];
|
||||
}
|
||||
return [`${pct.toFixed(2)}%`, '涨跌幅'];
|
||||
}
|
||||
if (name === 'volume') return [`${(Number(value) / 100000000).toFixed(2)}亿`, '成交量'];
|
||||
return [value, name];
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 零轴参考线 */}
|
||||
<ReferenceLine yAxisId="price" y={0} stroke="#666" strokeDasharray="4 4" />
|
||||
|
||||
{/* 成交量柱状图 */}
|
||||
<Bar
|
||||
yAxisId="volume"
|
||||
dataKey="volume"
|
||||
fill={`url(#barGradient-${title.replace(/[.\s]/g, '')})`}
|
||||
radius={[2, 2, 0, 0]}
|
||||
isAnimationActive={false}
|
||||
barSize={20}
|
||||
>
|
||||
{series.map((entry, index) => (
|
||||
<Cell
|
||||
key={`cell-${index}`}
|
||||
fill={index <= cursorIndex ? `url(#barGradientActive-${title.replace(/[.\s]/g, '')})` : `url(#barGradient-${title.replace(/[.\s]/g, '')})`}
|
||||
/>
|
||||
))}
|
||||
</Bar>
|
||||
|
||||
{/* 价格折线 */}
|
||||
<Line
|
||||
yAxisId="price"
|
||||
isAnimationActive={false}
|
||||
type="monotone"
|
||||
dataKey="value"
|
||||
stroke={color}
|
||||
strokeWidth={2}
|
||||
dot={false}
|
||||
/>
|
||||
|
||||
{/* 移动的亮点 - 使用 ReferenceDot 贴合主数据坐标系 */}
|
||||
{activePoint && (
|
||||
<ReferenceDot
|
||||
xAxisId={0}
|
||||
yAxisId="price"
|
||||
x={activePoint.time}
|
||||
y={activePoint.value}
|
||||
r={6}
|
||||
isFront
|
||||
ifOverflow="hidden"
|
||||
shape={(props) => (
|
||||
<g>
|
||||
<circle
|
||||
cx={props.cx}
|
||||
cy={props.cy}
|
||||
r={8}
|
||||
fill={color}
|
||||
stroke="#fff"
|
||||
strokeWidth={2}
|
||||
filter={`url(#glow-${title.replace(/[.\s]/g, '')})`}
|
||||
/>
|
||||
</g>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</ComposedChart>
|
||||
</ResponsiveContainer>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
// 数据流动线条组件
|
||||
function DataStreams() {
|
||||
const lines = useMemo(() => {
|
||||
return [...Array(15)].map((_, i) => ({
|
||||
id: i,
|
||||
startX: Math.random() * 100,
|
||||
delay: Math.random() * 5,
|
||||
duration: 3 + Math.random() * 2,
|
||||
height: 30 + Math.random() * 70
|
||||
}));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Box position="absolute" inset={0} overflow="hidden" pointerEvents="none">
|
||||
{lines.map((line) => (
|
||||
<motion.div
|
||||
key={line.id}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
width: '1px',
|
||||
background: 'linear-gradient(to bottom, transparent, rgba(255, 215, 0, 0.3), transparent)',
|
||||
left: `${line.startX}%`,
|
||||
height: `${line.height}%`,
|
||||
}}
|
||||
initial={{ y: '-100%', opacity: 0 }}
|
||||
animate={{
|
||||
y: '200%',
|
||||
opacity: [0, 0.5, 0.5, 0]
|
||||
}}
|
||||
transition={{
|
||||
duration: line.duration,
|
||||
delay: line.delay,
|
||||
repeat: Infinity,
|
||||
ease: "linear"
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
// 主组件
|
||||
export default function MidjourneyHeroSection() {
|
||||
const [sse, setSse] = useState({
|
||||
sh: { data: [], base: null },
|
||||
sz: { data: [], base: null },
|
||||
cyb: { data: [], base: null }
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const [shTL, szTL, cybTL, shDaily, szDaily, cybDaily] = await Promise.all([
|
||||
// 指数不传 event_time,后端自动返回"最新可用"交易日
|
||||
indexService.getKlineData('000001.SH', 'timeline'),
|
||||
indexService.getKlineData('399001.SZ', 'timeline'),
|
||||
indexService.getKlineData('399006.SZ', 'timeline'), // 创业板指
|
||||
indexService.getKlineData('000001.SH', 'daily'),
|
||||
indexService.getKlineData('399001.SZ', 'daily'),
|
||||
indexService.getKlineData('399006.SZ', 'daily'),
|
||||
]);
|
||||
|
||||
const shPrevClose = getPrevClose(shDaily);
|
||||
const szPrevClose = getPrevClose(szDaily);
|
||||
const cybPrevClose = getPrevClose(cybDaily);
|
||||
|
||||
const shSeries = toLineSeries(shTL);
|
||||
const szSeries = toLineSeries(szTL);
|
||||
const cybSeries = toLineSeries(cybTL);
|
||||
|
||||
const baseSh = (typeof shPrevClose === 'number' && isFinite(shPrevClose))
|
||||
? shPrevClose
|
||||
: (shSeries.length ? shSeries[0].value : 1);
|
||||
const baseSz = (typeof szPrevClose === 'number' && isFinite(szPrevClose))
|
||||
? szPrevClose
|
||||
: (szSeries.length ? szSeries[0].value : 1);
|
||||
const baseCyb = (typeof cybPrevClose === 'number' && isFinite(cybPrevClose))
|
||||
? cybPrevClose
|
||||
: (cybSeries.length ? cybSeries[0].value : 1);
|
||||
|
||||
const shPct = shSeries.map(p => ({
|
||||
time: p.time,
|
||||
value: ((p.value / baseSh) - 1) * 100,
|
||||
volume: p.volume || 0
|
||||
}));
|
||||
const szPct = szSeries.map(p => ({
|
||||
time: p.time,
|
||||
value: ((p.value / baseSz) - 1) * 100,
|
||||
volume: p.volume || 0
|
||||
}));
|
||||
const cybPct = cybSeries.map(p => ({
|
||||
time: p.time,
|
||||
value: ((p.value / baseCyb) - 1) * 100,
|
||||
volume: p.volume || 0
|
||||
}));
|
||||
|
||||
setSse({
|
||||
sh: { data: shPct, base: baseSh },
|
||||
sz: { data: szPct, base: baseSz },
|
||||
cyb: { data: cybPct, base: baseCyb }
|
||||
});
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
};
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
const particlesInit = async (engine) => {
|
||||
await loadSlim(engine);
|
||||
};
|
||||
|
||||
const particlesOptions = {
|
||||
particles: {
|
||||
number: {
|
||||
value: 80,
|
||||
density: {
|
||||
enable: true,
|
||||
value_area: 800
|
||||
}
|
||||
},
|
||||
color: {
|
||||
value: ["#FFD700", "#FF9800", "#FFC107", "#FFEB3B"]
|
||||
},
|
||||
shape: {
|
||||
type: "circle"
|
||||
},
|
||||
opacity: {
|
||||
value: 0.3,
|
||||
random: true,
|
||||
anim: {
|
||||
enable: true,
|
||||
speed: 1,
|
||||
opacity_min: 0.1,
|
||||
sync: false
|
||||
}
|
||||
},
|
||||
size: {
|
||||
value: 2,
|
||||
random: true,
|
||||
anim: {
|
||||
enable: true,
|
||||
speed: 2,
|
||||
size_min: 0.1,
|
||||
sync: false
|
||||
}
|
||||
},
|
||||
line_linked: {
|
||||
enable: true,
|
||||
distance: 150,
|
||||
color: "#FFD700",
|
||||
opacity: 0.2,
|
||||
width: 1
|
||||
},
|
||||
move: {
|
||||
enable: true,
|
||||
speed: 0.5,
|
||||
direction: "none",
|
||||
random: false,
|
||||
straight: false,
|
||||
out_mode: "out",
|
||||
bounce: false,
|
||||
}
|
||||
},
|
||||
interactivity: {
|
||||
detect_on: "canvas",
|
||||
events: {
|
||||
onhover: {
|
||||
enable: true,
|
||||
mode: "grab"
|
||||
},
|
||||
onclick: {
|
||||
enable: true,
|
||||
mode: "push"
|
||||
},
|
||||
resize: true
|
||||
},
|
||||
modes: {
|
||||
grab: {
|
||||
distance: 140,
|
||||
line_linked: {
|
||||
opacity: 0.5
|
||||
}
|
||||
},
|
||||
push: {
|
||||
particles_nb: 4
|
||||
}
|
||||
}
|
||||
},
|
||||
retina_detect: true
|
||||
};
|
||||
|
||||
const containerVariants = {
|
||||
hidden: { opacity: 0 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
transition: {
|
||||
staggerChildren: 0.1
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const itemVariants = {
|
||||
hidden: { opacity: 0, y: 20 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
duration: 0.6,
|
||||
ease: "easeOut"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
position="relative"
|
||||
minH="100vh"
|
||||
bg="linear-gradient(135deg, #0f0f0f 0%, #1a1a1a 50%, #000000 100%)"
|
||||
overflow="hidden"
|
||||
pointerEvents="none"
|
||||
>
|
||||
{/* 粒子背景 */}
|
||||
<Box position="absolute" inset={0} zIndex={-1} pointerEvents="none">
|
||||
<Particles
|
||||
id="tsparticles"
|
||||
init={particlesInit}
|
||||
options={particlesOptions}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* 数据流动效果 */}
|
||||
<DataStreams />
|
||||
|
||||
{/* 内容容器 */}
|
||||
<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">
|
||||
|
||||
{/* 左侧文本内容 */}
|
||||
<GridItem>
|
||||
<motion.div
|
||||
variants={containerVariants}
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
>
|
||||
<VStack align="start" spacing={6}>
|
||||
{/* 标签 */}
|
||||
<motion.div variants={itemVariants}>
|
||||
<Badge
|
||||
colorScheme="yellow"
|
||||
variant="subtle"
|
||||
px={4}
|
||||
py={2}
|
||||
borderRadius="full"
|
||||
fontSize="sm"
|
||||
fontFamily="monospace"
|
||||
display="inline-flex"
|
||||
alignItems="center"
|
||||
>
|
||||
<Box
|
||||
as="span"
|
||||
w={2}
|
||||
h={2}
|
||||
bg="yellow.400"
|
||||
borderRadius="full"
|
||||
mr={2}
|
||||
animation="pulse 2s ease-in-out infinite"
|
||||
/>
|
||||
AI-Assisted Curation
|
||||
</Badge>
|
||||
</motion.div>
|
||||
|
||||
{/* 主标题 */}
|
||||
<motion.div variants={itemVariants}>
|
||||
<Heading
|
||||
fontSize={{ base: '4xl', md: '5xl', lg: '6xl' }}
|
||||
fontWeight="bold"
|
||||
lineHeight="shorter"
|
||||
>
|
||||
<Text
|
||||
as="span"
|
||||
bgGradient="linear(to-r, yellow.400, orange.400, yellow.500)"
|
||||
bgClip="text"
|
||||
>
|
||||
ME-Agent
|
||||
</Text>
|
||||
<br />
|
||||
<Text as="span" color="white">
|
||||
实时分析系统
|
||||
</Text>
|
||||
</Heading>
|
||||
</motion.div>
|
||||
|
||||
{/* 副标题 */}
|
||||
<motion.div variants={itemVariants}>
|
||||
<Heading
|
||||
as="h3"
|
||||
fontSize="xl"
|
||||
color="gray.300"
|
||||
fontWeight="semibold"
|
||||
>
|
||||
基于微调版{' '}
|
||||
<Text as="span" color="yellow.400" fontFamily="monospace">
|
||||
deepseek-r1
|
||||
</Text>{' '}
|
||||
进行深度研究
|
||||
</Heading>
|
||||
</motion.div>
|
||||
|
||||
{/* 描述文本 */}
|
||||
<motion.div variants={itemVariants}>
|
||||
<Text
|
||||
color="gray.400"
|
||||
fontSize="md"
|
||||
lineHeight="tall"
|
||||
maxW="xl"
|
||||
>
|
||||
ME (Money Edge) 是一款以大模型为底座、由资深分析师参与校准的信息辅助系统,
|
||||
专为金融研究与企业决策等场景设计。系统侧重于多源信息的汇聚、清洗与结构化整理,
|
||||
结合自主训练的领域知识图谱,并配合专家人工复核与整合,帮助用户高效获取相关线索与参考资料。
|
||||
</Text>
|
||||
</motion.div>
|
||||
|
||||
{/* 特性标签 */}
|
||||
<motion.div variants={itemVariants}>
|
||||
<HStack spacing={3} flexWrap="wrap">
|
||||
{['海量信息整理', '领域知识图谱', '分析师复核', '结构化呈现'].map((tag) => (
|
||||
<Tag
|
||||
key={tag}
|
||||
size="md"
|
||||
variant="subtle"
|
||||
colorScheme="gray"
|
||||
borderRadius="lg"
|
||||
px={3}
|
||||
py={1}
|
||||
bg="gray.800"
|
||||
color="gray.300"
|
||||
borderWidth="1px"
|
||||
borderColor="gray.600"
|
||||
>
|
||||
{tag}
|
||||
</Tag>
|
||||
))}
|
||||
</HStack>
|
||||
</motion.div>
|
||||
|
||||
{/* 按钮组 */}
|
||||
<motion.div variants={itemVariants}>
|
||||
<HStack spacing={4} pt={4}>
|
||||
<Button
|
||||
size="lg"
|
||||
variant="outline"
|
||||
color="gray.300"
|
||||
borderColor="gray.600"
|
||||
borderRadius="full"
|
||||
px={8}
|
||||
_hover={{
|
||||
bg: "gray.800",
|
||||
borderColor: "gray.500",
|
||||
}}
|
||||
transition="all 0.2s"
|
||||
>
|
||||
了解更多
|
||||
</Button>
|
||||
</HStack>
|
||||
</motion.div>
|
||||
|
||||
{/* 统计数据 */}
|
||||
<motion.div variants={itemVariants}>
|
||||
<Grid
|
||||
templateColumns="repeat(3, 1fr)"
|
||||
gap={6}
|
||||
pt={8}
|
||||
borderTop="1px"
|
||||
borderTopColor="gray.800"
|
||||
w="full"
|
||||
>
|
||||
{[
|
||||
{ label: '数据源', value: '10K+' },
|
||||
{ label: '日处理', value: '1M+' },
|
||||
{ label: '准确率', value: '98%' }
|
||||
].map((stat) => (
|
||||
<Stat key={stat.label}>
|
||||
<StatNumber
|
||||
fontSize="2xl"
|
||||
fontWeight="bold"
|
||||
color="yellow.400"
|
||||
fontFamily="monospace"
|
||||
>
|
||||
{stat.value}
|
||||
</StatNumber>
|
||||
<StatLabel fontSize="sm" color="gray.500">
|
||||
{stat.label}
|
||||
</StatLabel>
|
||||
</Stat>
|
||||
))}
|
||||
</Grid>
|
||||
</motion.div>
|
||||
</VStack>
|
||||
</motion.div>
|
||||
</GridItem>
|
||||
|
||||
{/* 右侧金融图表可视化 */}
|
||||
<GridItem>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 1, delay: 0.5 }}
|
||||
>
|
||||
<Box position="relative" h={{ base: '400px', md: '500px', lg: '600px' }}>
|
||||
{/* 图表网格布局 */}
|
||||
<Grid
|
||||
templateColumns="repeat(2, 1fr)"
|
||||
templateRows="repeat(2, 1fr)"
|
||||
gap={4}
|
||||
h="full"
|
||||
p={4}
|
||||
bg="rgba(0, 0, 0, 0.3)"
|
||||
borderRadius="xl"
|
||||
border="1px solid"
|
||||
borderColor="rgba(255, 215, 0, 0.2)"
|
||||
backdropFilter="blur(10px)"
|
||||
>
|
||||
{/* 上证指数 */}
|
||||
<GridItem colSpan={2}>
|
||||
<Box
|
||||
h="full"
|
||||
bg="rgba(0, 0, 0, 0.4)"
|
||||
borderRadius="lg"
|
||||
p={2}
|
||||
border="1px solid"
|
||||
borderColor="rgba(255, 215, 0, 0.1)"
|
||||
>
|
||||
<CombinedChart
|
||||
series={sse.sh?.data || []}
|
||||
basePrice={sse.sh?.base}
|
||||
title="000001.SH 上证指数"
|
||||
color="#FFD700"
|
||||
/>
|
||||
</Box>
|
||||
</GridItem>
|
||||
|
||||
{/* 深证成指 */}
|
||||
<GridItem>
|
||||
<Box
|
||||
h="full"
|
||||
bg="rgba(0, 0, 0, 0.4)"
|
||||
borderRadius="lg"
|
||||
p={2}
|
||||
border="1px solid"
|
||||
borderColor="rgba(255, 215, 0, 0.1)"
|
||||
>
|
||||
<CombinedChart
|
||||
series={sse.sz?.data || []}
|
||||
basePrice={sse.sz?.base}
|
||||
title="399001.SZ 深证成指"
|
||||
color="#00E0FF"
|
||||
/>
|
||||
</Box>
|
||||
</GridItem>
|
||||
|
||||
{/* 创业板指 */}
|
||||
<GridItem>
|
||||
<Box
|
||||
h="full"
|
||||
bg="rgba(0, 0, 0, 0.4)"
|
||||
borderRadius="lg"
|
||||
p={2}
|
||||
border="1px solid"
|
||||
borderColor="rgba(255, 215, 0, 0.1)"
|
||||
>
|
||||
<CombinedChart
|
||||
series={sse.cyb?.data || []}
|
||||
basePrice={sse.cyb?.base}
|
||||
title="399006.SZ 创业板指"
|
||||
color="#FF69B4"
|
||||
/>
|
||||
</Box>
|
||||
</GridItem>
|
||||
</Grid>
|
||||
|
||||
{/* 装饰性光效 */}
|
||||
<Box
|
||||
position="absolute"
|
||||
top="50%"
|
||||
left="50%"
|
||||
transform="translate(-50%, -50%)"
|
||||
w="150%"
|
||||
h="150%"
|
||||
pointerEvents="none"
|
||||
>
|
||||
<Box
|
||||
position="absolute"
|
||||
top="20%"
|
||||
left="20%"
|
||||
w="200px"
|
||||
h="200px"
|
||||
bg="radial-gradient(circle, rgba(255, 215, 0, 0.15), transparent)"
|
||||
borderRadius="full"
|
||||
filter="blur(40px)"
|
||||
animation="pulse 4s ease-in-out infinite"
|
||||
/>
|
||||
<Box
|
||||
position="absolute"
|
||||
bottom="20%"
|
||||
right="20%"
|
||||
w="150px"
|
||||
h="150px"
|
||||
bg="radial-gradient(circle, rgba(255, 152, 0, 0.15), transparent)"
|
||||
borderRadius="full"
|
||||
filter="blur(40px)"
|
||||
animation="pulse 4s ease-in-out infinite"
|
||||
sx={{ animationDelay: '2s' }}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</motion.div>
|
||||
</GridItem>
|
||||
</Grid>
|
||||
</Container>
|
||||
|
||||
{/* 底部渐变遮罩 */}
|
||||
<Box
|
||||
position="absolute"
|
||||
bottom={0}
|
||||
left={0}
|
||||
right={0}
|
||||
h="128px"
|
||||
bgGradient="linear(to-t, black, transparent)"
|
||||
zIndex={-1}
|
||||
/>
|
||||
|
||||
{/* 全局样式 */}
|
||||
<style>{`
|
||||
@keyframes spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 0.4; transform: scale(1); }
|
||||
50% { opacity: 0.6; transform: scale(1.1); }
|
||||
}
|
||||
`}</style>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -20,7 +20,6 @@ import { useNavigate } from 'react-router-dom';
|
||||
import heroBg from '../../assets/img/BackgroundCard1.png';
|
||||
import '../../styles/home-animations.css';
|
||||
import { logger } from '../../utils/logger';
|
||||
import MidjourneyHeroSection from '../Community/components/MidjourneyHeroSection';
|
||||
import { usePostHogTrack } from '../../hooks/usePostHogRedux';
|
||||
import { ACQUISITION_EVENTS } from '../../lib/constants';
|
||||
|
||||
@@ -377,10 +376,6 @@ export default function HomePage() {
|
||||
</SimpleGrid>
|
||||
</VStack>
|
||||
</Box>
|
||||
|
||||
|
||||
{/* Midjourney风格英雄区域 */}
|
||||
<MidjourneyHeroSection />
|
||||
</VStack>
|
||||
</Container>
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user