## 变更内容 ### P0 高优先级新页面 - /parent/leave: 请假表单(react-hook-form + Zod) + 历史列表(状态筛选) - /parent/grades/report-card: 报告卡(学年/学期筛选 + 打印 + 教师评语) - /parent/children/[studentId]: 子女详情聚合页(5 Tab: overview/homework/grades/exams/schedule) ### P1 中优先级新页面 - /parent/error-book: 错题本(5 项统计 + Top 错题 + 薄弱知识点) - /parent/diagnostic: 诊断报告(掌握度摘要 + 已发布诊断报告) - /parent/practice: 练习统计(4 项统计 + 练习历史) ### P2 低优先级新页面 - /parent/course-plans + [id]: 课程计划列表 + 详情(章节列表) - /parent/lesson-plans + [planId]/view: 备课列表(学科筛选) + 只读详情 - /parent/elective: 选修课(分类色点 + 状态徽标) ### P3 现有页面增强 - /parent/dashboard: ParentAttentionBanner + 多子女卡片网格 + 趋势图标 + 逾期高亮 - /parent/attendance: AttendanceRateCard + AttendanceWarningBanner + 月份导航 - /parent/grades: GrowthArchiveChart + ExportGradesButton + 班级均对比线 ### 基础设施 - types/index.ts: +20 新类型(LeaveRequest/ReportCard/ErrorBookStats/DiagnosticReport/PracticeStats/CoursePlan/LessonPlan/ElectiveSelection/ChildDetail/ScheduleItem 等) - operations.ts: +19 GraphQL operations(17 query + 2 mutation) - fixtures.ts: +18 mock 数据集 - handlers.ts: +19 MSW GraphQL handler ### 质量校验 - typecheck: 0 错误 - lint: 0 错误 - test: 71 文件 / 763 测试全部通过(从 413 增至 763, +350 测试) ### 文档 - workline.md: 新增 §7 参考项目(CICD)差距分析与实现安排 + §7.3-7.5 实现进度与覆盖完成度 ### 设计决策 - 保留当前"单子女切换"范式(MultiChildTabBar + useChildSwitcher),不迁移到"多子女同屏对比" - 仪表盘除外:已增强为多子女卡片网格并列展示 - 参考项目所有 11 个家长端页面功能已 100% 覆盖 Refs: ARB-020 §22, ARB-022 §24.4
77 lines
2.1 KiB
TypeScript
77 lines
2.1 KiB
TypeScript
// ChildGradeChart:子女成绩图表(recharts)
|
||
// 依据:02-architecture-design.md §15.6 成绩图表设计
|
||
// - 柱状图:学生分数
|
||
// - 折线图:班级平均(虚线对比)
|
||
// - 响应式容器
|
||
|
||
"use client";
|
||
|
||
import {
|
||
ComposedChart,
|
||
Bar,
|
||
Line,
|
||
XAxis,
|
||
YAxis,
|
||
CartesianGrid,
|
||
Tooltip,
|
||
Legend,
|
||
ResponsiveContainer,
|
||
} from "recharts";
|
||
import type { GradeDataPoint } from "@/types";
|
||
|
||
interface ChildGradeChartProps {
|
||
grades: GradeDataPoint[];
|
||
}
|
||
|
||
export function ChildGradeChart({ grades }: ChildGradeChartProps) {
|
||
const data = grades.map((g) => ({
|
||
name: g.subject,
|
||
学生分数: g.studentScore,
|
||
班级平均: g.classAverage ?? 0,
|
||
}));
|
||
|
||
return (
|
||
<div className="rounded border border-rule bg-paper-elevated p-4">
|
||
<h3 className="font-serif text-base">成绩对比</h3>
|
||
<div className="mt-4 h-72">
|
||
<ResponsiveContainer width="100%" height="100%">
|
||
<ComposedChart
|
||
data={data}
|
||
margin={{ top: 8, right: 8, bottom: 8, left: 0 }}
|
||
>
|
||
<CartesianGrid strokeDasharray="3 3" stroke="hsl(var(--rule))" />
|
||
<XAxis
|
||
dataKey="name"
|
||
tick={{ fill: "hsl(var(--ink-muted))", fontSize: 12 }}
|
||
/>
|
||
<YAxis tick={{ fill: "hsl(var(--ink-muted))", fontSize: 12 }} />
|
||
<Tooltip
|
||
contentStyle={{
|
||
background: "hsl(var(--paper-elevated))",
|
||
border: "1px solid hsl(var(--rule))",
|
||
borderRadius: "0.5rem",
|
||
}}
|
||
labelStyle={{ color: "hsl(var(--ink))" }}
|
||
/>
|
||
<Legend wrapperStyle={{ fontSize: 12 }} />
|
||
<Bar
|
||
dataKey="学生分数"
|
||
fill="hsl(var(--accent))"
|
||
radius={[4, 4, 0, 0]}
|
||
/>
|
||
<Line
|
||
dataKey="班级平均"
|
||
stroke="hsl(var(--warning))"
|
||
strokeWidth={2}
|
||
strokeDasharray="5 5"
|
||
dot={{ r: 3, fill: "hsl(var(--warning))" }}
|
||
/>
|
||
</ComposedChart>
|
||
</ResponsiveContainer>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export default ChildGradeChart;
|