"use client"; import { useRef, useEffect } from "react"; import { Spin, Skeleton } from "antd"; import ReactECharts from "echarts-for-react"; import type { ECharts } from "echarts"; import { MetricToggle } from "@/components/ui/metric-toggle"; import { useTranslation } from "react-i18next"; import { BarChartOutlined } from "@ant-design/icons"; import { Card as ShadcnCard } from "@/components/ui/card"; import { motion } from "framer-motion"; interface UserUsage { nickname: string; total_cost: number; total_count: number; } interface UserRankingChartProps { loading: boolean; users: UserUsage[]; metric: "cost" | "count"; onMetricChange: (metric: "cost" | "count") => void; } const getBarOption = ( users: UserUsage[], metric: "cost" | "count", t: (key: string) => string ) => { const columnData = users .map((item) => ({ nickname: item.nickname, value: metric === "cost" ? Number(item.total_cost) : item.total_count, })) .sort((a, b) => b.value - a.value); const isSmallScreen = window.innerWidth < 640; return { tooltip: { show: false, }, grid: { top: isSmallScreen ? "8%" : "4%", bottom: isSmallScreen ? "2%" : "1%", left: "4%", right: "4%", containLabel: true, }, xAxis: { type: "category", data: columnData.map((item) => item.nickname.length > 12 ? item.nickname.slice(0, 10) + "..." : item.nickname ), axisLabel: { inside: false, color: "#555", fontSize: 12, rotate: 35, interval: 0, hideOverlap: true, padding: [0, 0, 0, 0], verticalAlign: "middle", align: "right", margin: 8, }, axisTick: { show: false, }, axisLine: { show: true, lineStyle: { color: "#eee", width: 2, }, }, z: 10, }, yAxis: { type: "value", name: "", nameTextStyle: { color: "#666", fontSize: 13, padding: [0, 0, 0, 0], }, axisLine: { show: true, lineStyle: { color: "#eee", width: 2, }, }, axisTick: { show: true, lineStyle: { color: "#eee", }, }, splitLine: { show: true, lineStyle: { color: "#f5f5f5", width: 2, }, }, axisLabel: { color: "#666", fontSize: 12, formatter: (value: number) => { if (metric === "cost") { return `¥${value.toFixed(1)}`; } return `${value}次`; }, }, }, dataZoom: [ { type: "inside", start: 0, end: Math.min(100, Math.max(100 * (15 / columnData.length), 40)), zoomLock: true, moveOnMouseMove: true, }, ], series: [ { type: "bar", itemStyle: { color: { type: "linear", x: 0, y: 0, x2: 0, y2: 1, colorStops: [ { offset: 0, color: "rgba(99, 133, 255, 0.85)", }, { offset: 1, color: "rgba(99, 133, 255, 0.4)", }, ], }, borderRadius: [8, 8, 0, 0], }, emphasis: { itemStyle: { color: { type: "linear", x: 0, y: 0, x2: 0, y2: 1, colorStops: [ { offset: 0, color: "rgba(99, 133, 255, 0.95)", }, { offset: 1, color: "rgba(99, 133, 255, 0.5)", }, ], }, shadowBlur: 10, shadowColor: "rgba(99, 133, 255, 0.2)", }, }, barWidth: "35%", data: columnData.map((item) => item.value), showBackground: true, backgroundStyle: { color: "rgba(180, 180, 180, 0.08)", borderRadius: [8, 8, 0, 0], }, label: { show: !isSmallScreen, position: "top", formatter: (params: any) => { return metric === "cost" ? `${params.value.toFixed(2)}` : `${params.value}`; }, fontSize: 11, color: "#666", distance: 2, fontFamily: "monospace", }, }, ], animation: true, animationDuration: 800, animationEasing: "cubicOut" as const, }; }; export default function UserRankingChart({ loading, users, metric, onMetricChange, }: UserRankingChartProps) { const { t } = useTranslation("common"); const chartRef = useRef(); useEffect(() => { const handleResize = () => { if (chartRef.current) { chartRef.current.resize(); chartRef.current.setOption(getBarOption(users, metric, t)); } }; window.addEventListener("resize", handleResize); return () => window.removeEventListener("resize", handleResize); }, [metric, users, t]); const onChartReady = (instance: ECharts) => { chartRef.current = instance; const zoomSize = 6; let isZoomed = false; instance.on("click", (params) => { const dataLength = users.length; if (!isZoomed) { instance.dispatchAction({ type: "dataZoom", startValue: users[Math.max(params.dataIndex - zoomSize / 2, 0)].nickname, endValue: users[Math.min(params.dataIndex + zoomSize / 2, dataLength - 1)] .nickname, }); isZoomed = true; } else { instance.dispatchAction({ type: "dataZoom", start: 0, end: 100, }); isZoomed = false; } }); }; return (

{t("panel.userUsageChart.title")}

{loading ? (
) : (
)}
); }