"use client" import { useMemo, useState } from "react" interface SimpleLineChartProps { data: Record[] dataKey: string xAxisKey: string stroke?: string height?: number } export function SimpleLineChart({ data, dataKey, xAxisKey, stroke = "#3b82f6", height = 250, }: SimpleLineChartProps) { const [tooltip, setTooltip] = useState<{ x: number y: number label: string value: number } | null>(null) const padding = { top: 20, right: 20, bottom: 40, left: 50 } const chartWidth = 600 const chartHeight = height const innerWidth = chartWidth - padding.left - padding.right const innerHeight = chartHeight - padding.top - padding.bottom const { points, xLabels, yLabels } = useMemo(() => { if (data.length === 0) return { points: [], xLabels: [], yLabels: [] } const values = data.map(d => Number(d[dataKey]) || 0) const maxVal = Math.max(...values, 1) const yMax = Math.ceil(maxVal * 1.2) const step = innerWidth / Math.max(data.length - 1, 1) const pts = data.map((d, i) => ({ x: padding.left + i * step, y: padding.top + innerHeight - (Number(d[dataKey]) / yMax) * innerHeight, })) const xLbls = data.map((d, i) => ({ x: padding.left + i * step, label: String(d[xAxisKey] ?? ""), })) const yTickCount = 5 const yLbls = Array.from({ length: yTickCount + 1 }, (_, i) => { const val = Math.round((yMax / yTickCount) * i) return { y: padding.top + innerHeight - (val / yMax) * innerHeight, label: String(val), } }) return { points: pts, xLabels: xLbls, yLabels: yLbls } }, [data, dataKey, xAxisKey, innerWidth, innerHeight]) if (data.length === 0) { return (
暂无数据
) } const linePath = points .map((p, i) => `${i === 0 ? "M" : "L"} ${p.x} ${p.y}`) .join(" ") const areaPath = points.length > 0 ? `${linePath} L ${points[points.length - 1].x} ${padding.top + innerHeight} L ${points[0].x} ${padding.top + innerHeight} Z` : "" return (
{yLabels.map((yl, i) => ( {yl.label} ))} {points.map((p, i) => ( setTooltip({ x: p.x, y: p.y, label: String(data[i]?.[xAxisKey] ?? ""), value: Number(data[i]?.[dataKey]) || 0, }) } onMouseLeave={() => setTooltip(null)} /> ))} {xLabels.map((xl, i) => i % Math.ceil(xLabels.length / 7) === 0 || i === xLabels.length - 1 ? ( {xl.label} ) : null, )} {tooltip && (
{tooltip.label}
{tooltip.value}
)}
) }