"use client" import { Bar, BarChart, CartesianGrid, XAxis, YAxis, Cell } from "recharts" import { useTranslations } from "next-intl" import { ChartContainer, ChartTooltip, ChartTooltipContent, type ChartConfig, } from "@/shared/components/ui/chart" import { Card, CardContent, CardHeader, CardTitle } from "@/shared/components/ui/card" import { Badge } from "@/shared/components/ui/badge" import { cn } from "@/shared/lib/utils" import type { ChapterWeakness } from "@/modules/error-book/types" interface ChapterWeaknessChartProps { data: ChapterWeakness[] className?: string } interface ChapterChartPayload { name: string errorCount: number masteredCount: number masteryRate: number knowledgePointCount: number topKps: Array<{ knowledgePointName: string; errorCount: number }> } function isChapterChartPayload(v: unknown): v is ChapterChartPayload { if (typeof v !== "object" || v === null) return false // 从 unknown 转换:类型守卫内需要属性访问来校验字段类型 const obj = v as Record return ( typeof obj.name === "string" && typeof obj.errorCount === "number" && typeof obj.masteredCount === "number" && typeof obj.masteryRate === "number" && typeof obj.knowledgePointCount === "number" ) } /** * 章节薄弱度图表(哪些课在错) * 横向柱状图,按错题数降序 * 每个柱子可展开显示该章节下错得最多的知识点 */ export function ChapterWeaknessChart({ data, className }: ChapterWeaknessChartProps) { const t = useTranslations("errorBook") if (data.length === 0) return null const chartData = data.map((d) => ({ name: d.chapterTitle, errorCount: d.errorCount, masteredCount: d.masteredCount, masteryRate: Number((d.masteryRate * 100).toFixed(0)), knowledgePointCount: d.knowledgePointCount, topKps: d.topKnowledgePoints, })) const chartConfig: ChartConfig = { errorCount: { label: t("chapterChart.errorCount"), color: "var(--color-chart-2)", }, } return ( {t("chapterChart.title")} value.length > 10 ? `${value.slice(0, 10)}...` : value } /> { if (!isChapterChartPayload(payload)) return null return (
{payload.name}
{t("chapterChart.errorCount")}: {payload.errorCount} {t("chapterChart.masteredLabel")}: {payload.masteredCount}
{t("chapterChart.masteryRateLabel")}: {payload.masteryRate}% {t("chapterChart.knowledgePointCount")}: {payload.knowledgePointCount}
{payload.topKps && payload.topKps.length > 0 ? (
{t("chapterChart.weakKpsLabel")}
{payload.topKps.map((kp) => (
{kp.knowledgePointName} {kp.errorCount}
))}
) : null}
) }} /> } /> {chartData.map((entry, idx) => ( ))}
{/* 章节详情列表 */}
{data.slice(0, 5).map((chapter) => (
{chapter.chapterTitle} {t("chapterChart.knowledgePointBadge", { count: chapter.knowledgePointCount })}
{chapter.topKnowledgePoints.length > 0 ? (
{chapter.topKnowledgePoints.map((kp) => ( {kp.knowledgePointName} · {kp.errorCount} ))}
) : null}
{chapter.errorCount} {t("chapterChart.masteryRateLabel")} {Math.round(chapter.masteryRate * 100)}%
))}
) }