移除 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/responsive": "^3.12.0",
|
||||||
"@visx/scale": "^3.12.0",
|
"@visx/scale": "^3.12.0",
|
||||||
"@visx/text": "^3.12.0",
|
"@visx/text": "^3.12.0",
|
||||||
"@tsparticles/react": "^3.0.0",
|
|
||||||
"@tsparticles/slim": "^3.0.0",
|
|
||||||
"@visx/visx": "^3.12.0",
|
"@visx/visx": "^3.12.0",
|
||||||
"@visx/wordcloud": "^3.12.0",
|
"@visx/wordcloud": "^3.12.0",
|
||||||
"antd": "^5.27.4",
|
"antd": "^5.27.4",
|
||||||
@@ -57,6 +55,7 @@
|
|||||||
"react-github-btn": "^1.2.1",
|
"react-github-btn": "^1.2.1",
|
||||||
"react-icons": "^4.12.0",
|
"react-icons": "^4.12.0",
|
||||||
"react-input-pin-code": "^1.1.5",
|
"react-input-pin-code": "^1.1.5",
|
||||||
|
"react-is": "^19.0.0",
|
||||||
"react-just-parallax": "^3.1.16",
|
"react-just-parallax": "^3.1.16",
|
||||||
"react-markdown": "^10.1.0",
|
"react-markdown": "^10.1.0",
|
||||||
"react-redux": "^9.2.0",
|
"react-redux": "^9.2.0",
|
||||||
@@ -64,14 +63,12 @@
|
|||||||
"react-responsive-masonry": "^2.7.1",
|
"react-responsive-masonry": "^2.7.1",
|
||||||
"react-router-dom": "^6.30.1",
|
"react-router-dom": "^6.30.1",
|
||||||
"react-scripts": "^5.0.1",
|
"react-scripts": "^5.0.1",
|
||||||
"react-is": "^19.0.0",
|
|
||||||
"react-scroll": "^1.8.4",
|
"react-scroll": "^1.8.4",
|
||||||
"react-scroll-into-view": "^2.1.3",
|
"react-scroll-into-view": "^2.1.3",
|
||||||
"react-table": "^7.7.0",
|
"react-table": "^7.7.0",
|
||||||
"react-tagsinput": "3.19.0",
|
"react-tagsinput": "3.19.0",
|
||||||
"react-to-print": "^2.13.0",
|
|
||||||
"react-tsparticles": "^2.12.2",
|
|
||||||
"react-to-print": "^3.0.3",
|
"react-to-print": "^3.0.3",
|
||||||
|
"react-tsparticles": "^2.12.2",
|
||||||
"recharts": "^3.1.2",
|
"recharts": "^3.1.2",
|
||||||
"sass": "^1.49.9",
|
"sass": "^1.49.9",
|
||||||
"socket.io-client": "^4.7.4",
|
"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 heroBg from '../../assets/img/BackgroundCard1.png';
|
||||||
import '../../styles/home-animations.css';
|
import '../../styles/home-animations.css';
|
||||||
import { logger } from '../../utils/logger';
|
import { logger } from '../../utils/logger';
|
||||||
import MidjourneyHeroSection from '../Community/components/MidjourneyHeroSection';
|
|
||||||
import { usePostHogTrack } from '../../hooks/usePostHogRedux';
|
import { usePostHogTrack } from '../../hooks/usePostHogRedux';
|
||||||
import { ACQUISITION_EVENTS } from '../../lib/constants';
|
import { ACQUISITION_EVENTS } from '../../lib/constants';
|
||||||
|
|
||||||
@@ -377,10 +376,6 @@ export default function HomePage() {
|
|||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
</VStack>
|
</VStack>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|
||||||
{/* Midjourney风格英雄区域 */}
|
|
||||||
<MidjourneyHeroSection />
|
|
||||||
</VStack>
|
</VStack>
|
||||||
</Container>
|
</Container>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
Reference in New Issue
Block a user