import { useRef, useEffect, useState } from 'react'; import * as echarts from 'echarts'; const chartData = [ { value: 25, name: '校企益邦' }, { value: 25, name: '科研管理系统' }, { value: 25, name: '智慧党建平台' }, { value: 25, name: '实训管理平台' }, ]; const rotationNeeded = [45, -45, -135, -225]; function normalizeAngle(a) { return ((a % 360) + 360) % 360; } function shortestRotation(from, to) { const fromN = normalizeAngle(from); const toN = normalizeAngle(to); let diff = toN - fromN; if (diff > 180) diff -= 360; if (diff < -180) diff += 360; return from + diff; } /** 将 hex 颜色转为带透明度的 rgba */ function hexToRgba(hex, alpha) { const r = parseInt(hex.slice(1, 3), 16); const g = parseInt(hex.slice(3, 5), 16); const b = parseInt(hex.slice(5, 7), 16); return `rgba(${r},${g},${b},${alpha})`; } export default function RosePieChart({ onSelect, activeIndex, colors }) { const containerRef = useRef(null); const chartRef = useRef(null); const rotationRef = useRef(0); const [rotation, setRotation] = useState(0); // 默认回退颜色 const safeColors = colors?.length === 4 ? colors : ['#60ce93', '#0c86cf', '#aa3130', '#8b50ee']; useEffect(() => { if (!containerRef.current) return; chartRef.current = echarts.init(containerRef.current, null, { renderer: 'canvas' }); chartRef.current.setOption({ tooltip: { trigger: 'item', backgroundColor: 'rgba(10,22,40,0.95)', borderColor: 'rgba(59,130,246,0.3)', borderWidth: 1, textStyle: { color: '#e2e8f0', fontSize: 13 }, formatter: '{b}', }, series: [{ type: 'pie', radius: ['25%', '75%'], center: ['50%', '50%'], startAngle: 90, padAngle: 3, itemStyle: { borderRadius: 6, borderWidth: 0 }, label: { show: true, position: 'inside', color: '#e2e8f0', fontSize: 15, fontWeight: 'bold', rotate: 'tangential', formatter: '{b}', }, emphasis: { scaleSize: 12, itemStyle: { shadowBlur: 40, shadowColor: 'rgba(59,130,246,0.5)', borderColor: 'rgba(59,130,246,0.6)', borderWidth: 0, }, label: { fontSize: 14, fontWeight: 'bold', color: '#fff' }, }, data: chartData.map((d, i) => ({ ...d, itemStyle: { color: safeColors[i], opacity: 1, borderRadius: 6, borderWidth: 0 }, })), animationType: 'scale', animationEasing: 'elasticOut', animationDuration: 1200, }], }); const handleResize = () => chartRef.current?.resize(); window.addEventListener('resize', handleResize); return () => { window.removeEventListener('resize', handleResize); chartRef.current?.dispose(); chartRef.current = null; }; }, []); useEffect(() => { const chart = chartRef.current; if (!chart) return; const isActive = activeIndex !== null && activeIndex !== undefined; const target = isActive ? rotationNeeded[activeIndex] : 0; const newAngle = shortestRotation(rotationRef.current, target); rotationRef.current = newAngle; setRotation(newAngle); chart.setOption({ series: [{ data: chartData.map((d, i) => ({ ...d, label: { color: isActive && activeIndex === i ? '#fff' : safeColors[i] }, itemStyle: { color: isActive && activeIndex !== i ? hexToRgba(safeColors[i], 0.2) : safeColors[i], opacity: 1, borderRadius: 6, borderColor: 'rgba(15,23,42,0.8)', borderWidth: 0, }, })), }], }); }, [activeIndex]); useEffect(() => { const chart = chartRef.current; if (!chart) return; const handler = (params) => { if (onSelect && params.dataIndex !== undefined) { onSelect(params.dataIndex); } }; chart.on('click', handler); return () => chart.off('click', handler); }, [onSelect]); return (