chore: 移除 visx 图表库依赖
- 删除 src/components/VisxPieChart/ 组件(无引用) - 清理 DataVisualizationComponents.js 中的 visx 词云实现 - 词云功能统一使用 ECharts 实现 - 卸载 @visx/visx、@visx/responsive 等相关包(共 72 个) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -18,11 +18,6 @@
|
|||||||
"@splidejs/react-splide": "^0.7.12",
|
"@splidejs/react-splide": "^0.7.12",
|
||||||
"@tanstack/react-virtual": "^3.13.12",
|
"@tanstack/react-virtual": "^3.13.12",
|
||||||
"@tippyjs/react": "^4.2.6",
|
"@tippyjs/react": "^4.2.6",
|
||||||
"@visx/responsive": "^3.12.0",
|
|
||||||
"@visx/scale": "^3.12.0",
|
|
||||||
"@visx/text": "^3.12.0",
|
|
||||||
"@visx/visx": "^3.12.0",
|
|
||||||
"@visx/wordcloud": "^3.12.0",
|
|
||||||
"antd": "^5.27.4",
|
"antd": "^5.27.4",
|
||||||
"axios": "^1.10.0",
|
"axios": "^1.10.0",
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
import { useState } from "react";
|
|
||||||
import { Pie } from "@visx/shape";
|
|
||||||
import { Group } from "@visx/group";
|
|
||||||
import { Text } from "@visx/text";
|
|
||||||
import { useColorModeValue } from "@chakra-ui/react";
|
|
||||||
|
|
||||||
const VisxPieChart = ({ data, title, width }) => {
|
|
||||||
const [active, setActive] = useState(null);
|
|
||||||
const half = width / 2;
|
|
||||||
|
|
||||||
const textColor = useColorModeValue("gray.700", "white");
|
|
||||||
|
|
||||||
return (
|
|
||||||
<svg width={width} height={width}>
|
|
||||||
<Group top={half} left={half}>
|
|
||||||
<Pie
|
|
||||||
data={data}
|
|
||||||
pieValue={(data) => data.percentage}
|
|
||||||
outerRadius={half}
|
|
||||||
innerRadius={({ data }) => {
|
|
||||||
const size = active && active.name == data.name ? 12 : 8;
|
|
||||||
return half - size;
|
|
||||||
}}
|
|
||||||
padAngle={0.01}
|
|
||||||
>
|
|
||||||
{(pie) => {
|
|
||||||
return pie.arcs.map((arc) => {
|
|
||||||
return (
|
|
||||||
<g
|
|
||||||
key={arc.data.name}
|
|
||||||
onMouseEnter={() => setActive(arc.data)}
|
|
||||||
onMouseLeave={() => setActive(null)}
|
|
||||||
>
|
|
||||||
<path d={pie.path(arc)} fill={arc.data.color}></path>
|
|
||||||
</g>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
</Pie>
|
|
||||||
|
|
||||||
|
|
||||||
<>
|
|
||||||
|
|
||||||
|
|
||||||
<Text textAnchor="middle" fill={textColor} fontWeight="bold" fontSize={32} dy={20}>
|
|
||||||
{title}
|
|
||||||
</Text>
|
|
||||||
</>
|
|
||||||
|
|
||||||
</Group>
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default VisxPieChart;
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useMemo, useState, useEffect, useRef } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Card,
|
Card,
|
||||||
@@ -50,10 +50,6 @@ import {
|
|||||||
Treemap,
|
Treemap,
|
||||||
Area, AreaChart,
|
Area, AreaChart,
|
||||||
} from 'recharts';
|
} from 'recharts';
|
||||||
// 词云库 - 支持两种实现
|
|
||||||
import { Wordcloud } from '@visx/wordcloud';
|
|
||||||
import { scaleLog } from '@visx/scale';
|
|
||||||
import { Text as VisxText } from '@visx/text';
|
|
||||||
import ReactECharts from 'echarts-for-react';
|
import ReactECharts from 'echarts-for-react';
|
||||||
import 'echarts-wordcloud';
|
import 'echarts-wordcloud';
|
||||||
// 颜色配置
|
// 颜色配置
|
||||||
@@ -65,97 +61,7 @@ const CHART_COLORS = [
|
|||||||
// 词云颜色常量
|
// 词云颜色常量
|
||||||
const WORDCLOUD_COLORS = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEEAD'];
|
const WORDCLOUD_COLORS = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEEAD'];
|
||||||
|
|
||||||
// ==================== 词云组件实现 1: @visx/wordcloud ====================
|
// 词云组件 - 使用 ECharts 实现
|
||||||
// 使用 SVG 渲染,React 18 原生支持,配置灵活
|
|
||||||
const VisxWordCloud = ({ data }) => {
|
|
||||||
const [dimensions, setDimensions] = useState({ width: 0, height: 400 });
|
|
||||||
const containerRef = useRef(null);
|
|
||||||
|
|
||||||
// 监听容器尺寸变化
|
|
||||||
useEffect(() => {
|
|
||||||
if (!containerRef.current) return;
|
|
||||||
|
|
||||||
const updateDimensions = () => {
|
|
||||||
if (containerRef.current) {
|
|
||||||
setDimensions({
|
|
||||||
width: containerRef.current.offsetWidth,
|
|
||||||
height: 400
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
updateDimensions();
|
|
||||||
const resizeObserver = new ResizeObserver(updateDimensions);
|
|
||||||
resizeObserver.observe(containerRef.current);
|
|
||||||
|
|
||||||
return () => resizeObserver.disconnect();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
if (!data || data.length === 0) {
|
|
||||||
return (
|
|
||||||
<Center h="400px">
|
|
||||||
<VStack>
|
|
||||||
<Text color="gray.500">暂无词云数据</Text>
|
|
||||||
</VStack>
|
|
||||||
</Center>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const words = data.slice(0, 100).map(item => ({
|
|
||||||
name: item.name || item.text,
|
|
||||||
value: item.value || item.count || 1
|
|
||||||
}));
|
|
||||||
|
|
||||||
// 计算字体大小比例
|
|
||||||
const fontScale = scaleLog({
|
|
||||||
domain: [
|
|
||||||
Math.min(...words.map(w => w.value)),
|
|
||||||
Math.max(...words.map(w => w.value))
|
|
||||||
],
|
|
||||||
range: [16, 80],
|
|
||||||
});
|
|
||||||
|
|
||||||
const fontSizeSetter = (datum) => fontScale(datum.value);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box ref={containerRef} h="400px" w="100%">
|
|
||||||
{dimensions.width > 0 && (
|
|
||||||
<svg width={dimensions.width} height={dimensions.height}>
|
|
||||||
<Wordcloud
|
|
||||||
words={words}
|
|
||||||
width={dimensions.width}
|
|
||||||
height={dimensions.height}
|
|
||||||
fontSize={fontSizeSetter}
|
|
||||||
font="Microsoft YaHei, sans-serif"
|
|
||||||
padding={3}
|
|
||||||
spiral="archimedean"
|
|
||||||
rotate={0}
|
|
||||||
random={() => 0.5}
|
|
||||||
>
|
|
||||||
{(cloudWords) =>
|
|
||||||
cloudWords.map((w, i) => (
|
|
||||||
<VisxText
|
|
||||||
key={w.text}
|
|
||||||
fill={WORDCLOUD_COLORS[i % WORDCLOUD_COLORS.length]}
|
|
||||||
textAnchor="middle"
|
|
||||||
transform={`translate(${w.x}, ${w.y}) rotate(${w.rotate})`}
|
|
||||||
fontSize={w.size}
|
|
||||||
fontFamily={w.font}
|
|
||||||
fontWeight="bold"
|
|
||||||
>
|
|
||||||
{w.text}
|
|
||||||
</VisxText>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</Wordcloud>
|
|
||||||
</svg>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// ==================== 词云组件实现 2: ECharts Wordcloud ====================
|
|
||||||
// 使用 Canvas 渲染,内置交互效果(tooltip、emphasis),配置简单
|
|
||||||
const EChartsWordCloud = ({ data }) => {
|
const EChartsWordCloud = ({ data }) => {
|
||||||
if (!data || data.length === 0) {
|
if (!data || data.length === 0) {
|
||||||
return (
|
return (
|
||||||
@@ -216,9 +122,8 @@ const EChartsWordCloud = ({ data }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ==================== 词云组件包装器 ====================
|
// 词云组件
|
||||||
// 统一接口,支持切换两种实现方式
|
const WordCloud = ({ data }) => {
|
||||||
const WordCloud = ({ data, engine = 'echarts' }) => {
|
|
||||||
if (!data || data.length === 0) {
|
if (!data || data.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Center h="400px">
|
<Center h="400px">
|
||||||
@@ -229,8 +134,7 @@ const WordCloud = ({ data, engine = 'echarts' }) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据 engine 参数选择实现方式
|
return <EChartsWordCloud data={data} />;
|
||||||
return engine === 'visx' ? <VisxWordCloud data={data} /> : <EChartsWordCloud data={data} />;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 板块热力图组件
|
// 板块热力图组件
|
||||||
|
|||||||
Reference in New Issue
Block a user