import { useMemo, useState } from "react"; import { trpc } from "@/lib/trpc"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Skeleton } from "@/components/ui/skeleton"; import { useBackgroundTask } from "@/hooks/useBackgroundTask"; import { formatDateTimeShanghai, formatMonthDayShanghai } from "@/lib/time"; import { toast } from "sonner"; import { Activity, Award, Loader2, RefreshCw, Radar, TrendingUp } from "lucide-react"; import { Area, AreaChart, CartesianGrid, PolarAngleAxis, PolarGrid, PolarRadiusAxis, Radar as RadarChartShape, RadarChart, ResponsiveContainer, Tooltip, XAxis, YAxis, } from "recharts"; const NTRP_LEVELS = [ { min: 1.0, max: 1.5, label: "入门" }, { min: 1.5, max: 2.0, label: "初级" }, { min: 2.0, max: 2.5, label: "初中级" }, { min: 2.5, max: 3.0, label: "中级" }, { min: 3.0, max: 3.5, label: "中高级" }, { min: 3.5, max: 4.0, label: "高级" }, { min: 4.0, max: 4.5, label: "高级竞技" }, { min: 4.5, max: 5.1, label: "接近专业" }, ]; function getLevel(rating: number) { return NTRP_LEVELS.find((item) => rating >= item.min && rating < item.max)?.label || "入门"; } export default function Rating() { const [taskId, setTaskId] = useState(null); const currentQuery = trpc.rating.current.useQuery(); const historyQuery = trpc.rating.history.useQuery(); const refreshMineMutation = trpc.rating.refreshMine.useMutation({ onSuccess: (data) => { setTaskId(data.taskId); toast.success("NTRP 刷新任务已加入后台队列"); }, onError: (error) => toast.error(`NTRP 刷新失败: ${error.message}`), }); const taskQuery = useBackgroundTask(taskId); const currentRating = currentQuery.data?.rating || 1.5; const latestSnapshot = currentQuery.data?.latestSnapshot as any; const history = historyQuery.data ?? []; const radarData = useMemo(() => { const scores = latestSnapshot?.dimensionScores || {}; return [ { dimension: "姿态", value: scores.poseAccuracy || 0 }, { dimension: "一致性", value: scores.strokeConsistency || 0 }, { dimension: "脚步", value: scores.footwork || 0 }, { dimension: "流畅度", value: scores.fluidity || 0 }, { dimension: "时机", value: scores.timing || 0 }, { dimension: "比赛准备", value: scores.matchReadiness || 0 }, ]; }, [latestSnapshot?.dimensionScores]); const trendData = useMemo( () => history.map((item: any) => ({ date: formatMonthDayShanghai(item.createdAt), rating: item.rating, })).reverse(), [history], ); if (currentQuery.isLoading || historyQuery.isLoading) { return (
); } return (

NTRP 评分系统

评分由历史训练、实时分析、录制归档与动作质量共同计算。每日零点后会自动异步刷新,当前用户也可以手动提交刷新任务。

{(taskQuery.data?.status === "queued" || taskQuery.data?.status === "running") ? ( 后台执行中 {taskQuery.data.message || "NTRP 刷新任务正在后台执行。"} ) : null}
{currentRating.toFixed(1)}
{getLevel(currentRating)}
最新综合评分
NTRP {currentRating.toFixed(1)} {latestSnapshot?.triggerType ? 来源 {latestSnapshot.triggerType} : null} {latestSnapshot?.createdAt ? ( 刷新于 {formatDateTimeShanghai(latestSnapshot.createdAt)} ) : null}
训练日
{latestSnapshot?.sourceSummary?.activeDays || 0}
有效动作
{latestSnapshot?.sourceSummary?.totalEffectiveActions || 0}
实时分析
{latestSnapshot?.sourceSummary?.liveSessions || 0}
PK 会话
{latestSnapshot?.sourceSummary?.totalPk || 0}
评分维度 {radarData.map((item) => (
{item.dimension} {Math.round(item.value)}
))}
NTRP 趋势 {trendData.length === 0 ? (
暂无评分趋势数据。
) : ( )}
最新雷达图 按最近一次 NTRP 快照展示维度得分。
历史快照 这里展示异步评分任务生成的最新记录。 {history.length === 0 ? (
暂无历史快照。
) : ( history.map((item: any) => (
NTRP {Number(item.rating || 0).toFixed(1)} {item.triggerType}
{formatDateTimeShanghai(item.createdAt)}
分析 {item.sourceSummary?.analyses || 0} 实时 {item.sourceSummary?.liveSessions || 0} 动作 {item.sourceSummary?.totalEffectiveActions || 0}
)) )}
); }