update ui
This commit is contained in:
@@ -41,8 +41,6 @@ import {
|
|||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { TrendingUp, Activity, Globe, Zap } from 'lucide-react';
|
import { TrendingUp, Activity, Globe, Zap } from 'lucide-react';
|
||||||
import ReactECharts from 'echarts-for-react';
|
import ReactECharts from 'echarts-for-react';
|
||||||
import * as echarts from 'echarts';
|
|
||||||
import 'echarts-wordcloud'; // 导入词云插件
|
|
||||||
import { logger } from '../../../utils/logger';
|
import { logger } from '../../../utils/logger';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -389,13 +387,13 @@ const MiniIndexChart = ({ indexCode, indexName }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 概念词云图组件
|
* 概念流动动画组件(替代词云)
|
||||||
*/
|
*/
|
||||||
const ConceptWordCloud = () => {
|
const ConceptFlowAnimation = () => {
|
||||||
const [concepts, setConcepts] = useState([]);
|
const [concepts, setConcepts] = useState([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [isPaused, setIsPaused] = useState(false);
|
||||||
const chartBg = useColorModeValue('transparent', 'transparent');
|
const [hoveredConcept, setHoveredConcept] = useState(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let isMounted = true;
|
let isMounted = true;
|
||||||
@@ -418,94 +416,26 @@ const ConceptWordCloud = () => {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const chartOption = useMemo(() => {
|
// 根据涨跌幅获取颜色
|
||||||
if (concepts.length === 0) {
|
const getColor = (changePct) => {
|
||||||
logger.warn('ConceptWordCloud', 'No concepts data available');
|
if (changePct > 7) return '#ff0000'; // 超大涨:纯红
|
||||||
return {};
|
if (changePct > 5) return '#ff1744'; // 大涨:亮红色
|
||||||
}
|
if (changePct > 3) return '#ff4d4f'; // 中涨:红色
|
||||||
|
if (changePct > 1) return '#ff7875'; // 小涨:浅红
|
||||||
|
if (changePct > 0) return '#ffa940'; // 微涨:橙色
|
||||||
|
if (changePct === 0) return '#FFD700'; // 平盘:金色
|
||||||
|
if (changePct > -1) return '#73d13d'; // 微跌:浅绿
|
||||||
|
if (changePct > -3) return '#52c41a'; // 小跌:绿色
|
||||||
|
if (changePct > -5) return '#00c853'; // 中跌:深绿
|
||||||
|
if (changePct > -7) return '#00a152'; // 大跌:更深绿
|
||||||
|
return '#00796b'; // 超大跌:墨绿
|
||||||
|
};
|
||||||
|
|
||||||
logger.debug('ConceptWordCloud', 'Rendering word cloud with concepts', {
|
// 处理概念点击
|
||||||
count: concepts.length,
|
const handleConceptClick = (conceptName) => {
|
||||||
sample: concepts.slice(0, 3)
|
const url = `https://valuefrontier.cn/htmls/${conceptName}.html`;
|
||||||
});
|
window.open(url, '_blank');
|
||||||
|
};
|
||||||
return {
|
|
||||||
backgroundColor: chartBg,
|
|
||||||
tooltip: {
|
|
||||||
show: true,
|
|
||||||
backgroundColor: 'rgba(20, 20, 20, 0.95)',
|
|
||||||
borderColor: '#FFD700',
|
|
||||||
borderWidth: 1,
|
|
||||||
textStyle: {
|
|
||||||
color: '#fff',
|
|
||||||
fontSize: 12
|
|
||||||
},
|
|
||||||
formatter: (params) => {
|
|
||||||
const changePct = params.data.change_pct;
|
|
||||||
const sign = changePct > 0 ? '+' : '';
|
|
||||||
const color = changePct > 0 ? '#ec0000' : '#00da3c';
|
|
||||||
const icon = changePct > 0 ? '📈' : '📉';
|
|
||||||
return `
|
|
||||||
<div style="font-weight: bold; font-size: 14px; margin-bottom: 6px; border-bottom: 1px solid #FFD700; padding-bottom: 4px;">
|
|
||||||
${icon} ${params.name}
|
|
||||||
</div>
|
|
||||||
<div style="color: ${color}; font-weight: bold; font-size: 16px;">
|
|
||||||
涨跌幅: ${sign}${changePct.toFixed(2)}%
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
series: [{
|
|
||||||
type: 'wordCloud',
|
|
||||||
shape: 'circle',
|
|
||||||
left: 'center',
|
|
||||||
top: 'center',
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
right: null,
|
|
||||||
bottom: null,
|
|
||||||
sizeRange: [14, 42],
|
|
||||||
rotationRange: [-30, 30],
|
|
||||||
rotationStep: 15,
|
|
||||||
gridSize: 10,
|
|
||||||
drawOutOfBound: false,
|
|
||||||
layoutAnimation: true,
|
|
||||||
textStyle: {
|
|
||||||
fontFamily: 'sans-serif',
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: function (params) {
|
|
||||||
// 根据涨跌幅设置颜色(中国市场惯例:涨红跌绿)
|
|
||||||
const changePct = params.data.change_pct;
|
|
||||||
if (changePct > 7) return '#ff0000'; // 超大涨:纯红
|
|
||||||
if (changePct > 5) return '#ff1744'; // 大涨:亮红色
|
|
||||||
if (changePct > 3) return '#ff4d4f'; // 中涨:红色
|
|
||||||
if (changePct > 1) return '#ff7875'; // 小涨:浅红
|
|
||||||
if (changePct > 0) return '#ffa940'; // 微涨:橙色
|
|
||||||
if (changePct === 0) return '#FFD700'; // 平盘:金色
|
|
||||||
if (changePct > -1) return '#73d13d'; // 微跌:浅绿
|
|
||||||
if (changePct > -3) return '#52c41a'; // 小跌:绿色
|
|
||||||
if (changePct > -5) return '#00c853'; // 中跌:深绿
|
|
||||||
if (changePct > -7) return '#00a152'; // 大跌:更深绿
|
|
||||||
return '#00796b'; // 超大跌:墨绿
|
|
||||||
},
|
|
||||||
shadowBlur: 3,
|
|
||||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
|
||||||
shadowOffsetX: 1,
|
|
||||||
shadowOffsetY: 1
|
|
||||||
},
|
|
||||||
emphasis: {
|
|
||||||
focus: 'self',
|
|
||||||
textStyle: {
|
|
||||||
shadowBlur: 20,
|
|
||||||
shadowColor: '#FFD700',
|
|
||||||
fontSize: 28,
|
|
||||||
fontWeight: 'bolder'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data: concepts
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
}, [concepts, chartBg]);
|
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
@@ -525,15 +455,135 @@ const ConceptWordCloud = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 将概念分成3行
|
||||||
|
const rows = [
|
||||||
|
concepts.slice(0, 6),
|
||||||
|
concepts.slice(6, 12),
|
||||||
|
concepts.slice(12, 18)
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactECharts
|
<VStack spacing={2} h="200px" justify="center" overflow="hidden" position="relative" w="100%">
|
||||||
echarts={echarts}
|
{rows.map((row, rowIndex) => {
|
||||||
option={chartOption}
|
// 每行不同的速度:第一行20秒,第二行25秒,第三行30秒
|
||||||
style={{ height: '200px', width: '100%' }}
|
const duration = 25 + rowIndex * 5;
|
||||||
opts={{ renderer: 'canvas' }}
|
|
||||||
notMerge={true}
|
return (
|
||||||
lazyUpdate={true}
|
<Box
|
||||||
/>
|
key={rowIndex}
|
||||||
|
position="relative"
|
||||||
|
width="100%"
|
||||||
|
height="50px"
|
||||||
|
overflow="hidden"
|
||||||
|
>
|
||||||
|
<HStack
|
||||||
|
spacing={3}
|
||||||
|
position="absolute"
|
||||||
|
left={0}
|
||||||
|
h="100%"
|
||||||
|
align="center"
|
||||||
|
animation={!isPaused ? `flowRight${rowIndex} ${duration}s linear infinite` : 'none'}
|
||||||
|
_hover={{ animationPlayState: 'paused' }}
|
||||||
|
sx={{
|
||||||
|
[`@keyframes flowRight${rowIndex}`]: {
|
||||||
|
'0%': { transform: 'translateX(0)' },
|
||||||
|
'100%': { transform: 'translateX(-50%)' }
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* 渲染两次以实现无缝循环 */}
|
||||||
|
{[...row, ...row].map((concept, idx) => {
|
||||||
|
const changePct = concept.change_pct;
|
||||||
|
const color = getColor(changePct);
|
||||||
|
const sign = changePct > 0 ? '+' : '';
|
||||||
|
const icon = changePct > 0 ? '📈' : '📉';
|
||||||
|
const isHovered = hoveredConcept === `${rowIndex}-${idx}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
key={`${rowIndex}-${idx}`}
|
||||||
|
px={4}
|
||||||
|
py={2}
|
||||||
|
borderRadius="full"
|
||||||
|
bg="whiteAlpha.200"
|
||||||
|
borderWidth="2px"
|
||||||
|
borderColor={color}
|
||||||
|
color="white"
|
||||||
|
fontWeight="bold"
|
||||||
|
fontSize="sm"
|
||||||
|
whiteSpace="nowrap"
|
||||||
|
cursor="pointer"
|
||||||
|
position="relative"
|
||||||
|
transition="all 0.3s ease"
|
||||||
|
boxShadow={`0 0 10px ${color}40`}
|
||||||
|
onMouseEnter={() => {
|
||||||
|
setIsPaused(true);
|
||||||
|
setHoveredConcept(`${rowIndex}-${idx}`);
|
||||||
|
}}
|
||||||
|
onMouseLeave={() => {
|
||||||
|
setIsPaused(false);
|
||||||
|
setHoveredConcept(null);
|
||||||
|
}}
|
||||||
|
onClick={() => handleConceptClick(concept.name)}
|
||||||
|
_hover={{
|
||||||
|
transform: 'scale(1.15)',
|
||||||
|
bg: 'whiteAlpha.300',
|
||||||
|
boxShadow: `0 0 20px ${color}80`,
|
||||||
|
zIndex: 100
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* 概念名称 */}
|
||||||
|
<Text as="span" color={color}>
|
||||||
|
{concept.name}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
{/* 悬停时显示涨跌幅 */}
|
||||||
|
{isHovered && (
|
||||||
|
<Box
|
||||||
|
position="absolute"
|
||||||
|
top="-45px"
|
||||||
|
left="50%"
|
||||||
|
transform="translateX(-50%)"
|
||||||
|
bg="rgba(20, 20, 20, 0.95)"
|
||||||
|
borderWidth="1px"
|
||||||
|
borderColor="#FFD700"
|
||||||
|
borderRadius="md"
|
||||||
|
px={3}
|
||||||
|
py={2}
|
||||||
|
whiteSpace="nowrap"
|
||||||
|
boxShadow="0 4px 12px rgba(0,0,0,0.3)"
|
||||||
|
zIndex={200}
|
||||||
|
_before={{
|
||||||
|
content: '""',
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: '-6px',
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translateX(-50%)',
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
borderLeft: '6px solid transparent',
|
||||||
|
borderRight: '6px solid transparent',
|
||||||
|
borderTop: '6px solid #FFD700'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<VStack spacing={1}>
|
||||||
|
<Text fontSize="xs" color="white" fontWeight="bold">
|
||||||
|
{icon} {concept.name}
|
||||||
|
</Text>
|
||||||
|
<Text fontSize="sm" color={color} fontWeight="bold">
|
||||||
|
{sign}{changePct.toFixed(2)}%
|
||||||
|
</Text>
|
||||||
|
</VStack>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</HStack>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</VStack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -763,12 +813,14 @@ const HeroPanel = () => {
|
|||||||
boxShadow: "0 6px 24px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.1)"
|
boxShadow: "0 6px 24px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.1)"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ConceptWordCloud />
|
<ConceptFlowAnimation />
|
||||||
</Box>
|
</Box>
|
||||||
<HStack spacing={2} fontSize="xs" color="whiteAlpha.600" w="100%" justify="center">
|
<HStack spacing={2} fontSize="xs" color="whiteAlpha.600" w="100%" justify="center">
|
||||||
<Text>💡 字体大小表示热度</Text>
|
<Text>🌊 概念流动展示</Text>
|
||||||
<Text>•</Text>
|
<Text>•</Text>
|
||||||
<Text>🎨 颜色表示涨跌幅</Text>
|
<Text>🎨 颜色表示涨跌幅</Text>
|
||||||
|
<Text>•</Text>
|
||||||
|
<Text>👆 点击查看详情</Text>
|
||||||
</HStack>
|
</HStack>
|
||||||
</VStack>
|
</VStack>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
Reference in New Issue
Block a user