Files
NextEdu/bugs/parent_bug.md
SpecialX 49291fcc31 refactor: fix all P0/P1/P2 bugs and architecture issues
Bug fixes (from bugs/ directory):

- Fix cross-module DB queries in 9 modules (homework, grades, parent, diagnostic, elective, proctoring, notifications, scheduling, classes) by routing through data-access functions

- Fix shared/lib <-> auth circular dependency via new session.ts module

- Fix divide-by-zero guard in grades data-access

- Fix audit export data truncation (paginated fetch for full datasets)

- Fix missing transactions in homework grading and elective lottery

- Fix missing revalidatePath in course-plans actions

- Fix frontend permission checks using requirePermission instead of requireAuth

- Fix dashboard role routing using session.user.roles

- Fix student auth pattern (migrate getDemoStudentUser to users module)

- Fix ActionState return type handling in components

Code quality fixes:

- Remove 60+ as type assertions (replace with type guards)

- Remove non-null assertions (use optional chaining or explicit checks)

- Convert dynamic imports to static imports (grades, diagnostic)

- Add React.cache() wrapping for read functions

- Parallelize independent queries with Promise.all

- Add explicit return types to 30+ arrow functions

- Replace any with unknown + type guards

- Fix import type for type-only imports

- Add Zod validation schemas for classes and diagnostic modules

- Extract duplicate code (normalizeRoleName, normalizeBcryptHash, logger IP extraction)

- Add console.error to silent catch blocks

- Fix permission naming consistency (exam:proctor_read -> exam:proctor:read)

Architecture doc sync:

- Update 004_architecture_impact_map.md and 005_architecture_data.json

- Update management-modules-audit.md for P0-7 cross-module fix

Moved deleted proctoring event route to deletes/ folder.
2026-06-19 05:13:34 +08:00

30 KiB
Raw Blame History

src/app/(dashboard)/parent 前端规范核查报告

核查日期2026-06-18 核查范围:src/app/(dashboard)/parent/ 下所有前端文件 + src/modules/parent/ 配套组件与 data-access 依据文档:项目规则、编码规范 docs/standards/coding-standards.md、架构影响地图 004、架构数据 005 应用技能:vercel-react-best-practicesweb-artifacts-builderweb-design-guidelines


一、核查文件清单

1.1 路由页面文件(src/app/(dashboard)/parent/

文件 行数 类型 用途
dashboard/page.tsx 16 Server Component 家长仪表盘入口页
attendance/page.tsx 61 Server Component 子女考勤聚合页
grades/page.tsx 61 Server Component 子女成绩聚合页
children/[studentId]/page.tsx 71 Server Component 单个子女详情页

1.2 模块组件文件(src/modules/parent/components/

文件 行数 类型 用途
parent-dashboard.tsx 68 Server Component 仪表盘主组件
child-card.tsx 89 Server Component 子女卡片
child-detail-header.tsx 49 Server Component 详情页头部
child-detail-panel.tsx 27 Server Component 详情页面板
child-grade-summary.tsx 163 Client Component 成绩趋势图
child-homework-summary.tsx 131 Server Component 作业摘要
child-schedule-card.tsx 67 Server Component 今日课表

1.3 数据访问与类型(src/modules/parent/

文件 行数 类型 用途
data-access.ts 234 server-only 家长-子女数据聚合
types.ts 57 类型定义 模块类型

二、违规问题清单

2.1 children/[studentId]/page.tsx — 严重度:高(架构违规)

BUG-P001app 层直接访问 DB违反三层架构

  • 位置src/app/(dashboard)/parent/children/[studentId]/page.tsx:2-6, 24-31
  • 问题:页面直接 import { db }parentStudentRelations schema并执行 db.select().from(parentStudentRelations) 查询
  • 规范依据:项目规则「架构分层规则」明确「app/ 只能调用 modules/ 的 Server Actions 和 data-access不直接访问 DB」
  • 现状
    import { db } from "@/shared/db"
    import { parentStudentRelations } from "@/shared/db/schema"
    // ...
    const [relation] = await db
      .select({ id: parentStudentRelations.id, relation: parentStudentRelations.relation })
      .from(parentStudentRelations)
      .where(eq(parentStudentRelations.studentId, studentId))
      .limit(1)
    
  • 改进建议:在 parent/data-access.ts 新增 verifyParentChildRelation(studentId, parentId) 函数,页面调用该函数

BUG-P002权限校验存在信息泄露风险

  • 位置src/app/(dashboard)/parent/children/[studentId]/page.tsx:24-31
  • 问题:第一次查询 relation 时仅按 studentId 过滤,未加 parentId = ctx.userId 条件。任何登录用户都能探测任意 studentId 是否存在 parent 关系
  • 影响:信息泄露(可枚举 studentId 探测家庭关系)
  • 改进建议:查询条件加 and(eq(parentStudentRelations.studentId, studentId), eq(parentStudentRelations.parentId, ctx.userId))

BUG-P003两个 "Access denied" 分支重复

  • 位置src/app/(dashboard)/parent/children/[studentId]/page.tsx:33-58
  • 问题relation 不存在与 dataScope 不包含两个分支返回完全相同的 UI代码重复
  • 改进建议:合并为单一校验路径,或抽取 AccessDenied 组件

BUG-P004requireAuth() 未做角色校验

  • 位置src/app/(dashboard)/parent/children/[studentId]/page.tsx:21
  • 问题:使用 requireAuth() 而非 requirePermission(),未校验当前用户是否为 parent 角色。teacher/admin 也能访问该页面(虽然 dataScope 校验会拦截,但应前置失败)
  • 改进建议:使用 requirePermission(PARENT_VIEW) 或在 auth-guard 中增加角色校验

2.2 attendance/page.tsxgrades/page.tsx — 严重度:高(代码重复)

BUG-P005两个页面文件几乎完全重复

  • 位置src/app/(dashboard)/parent/attendance/page.tsxsrc/app/(dashboard)/parent/grades/page.tsx

  • 问题:两个文件结构 95% 相同仅模块名attendance vs grades、图标CalendarCheck vs GraduationCap、标题文案不同

  • 违反规则DRY 原则,编码规范「工具函数 ≤ 40 行」隐含的复用精神

  • 改进建议:抽取共享组件 ParentChildrenDataPage,通过 props 传入 fetchericontitleemptyTitlerenderItem

    // 抽取后的共享组件
    function ParentChildrenDataPage<T>({
      title, description, icon, fetcher, renderItem, emptyTitle, emptyDescription,
    }: ParentChildrenDataPageProps<T>) { /* ... */ }
    

BUG-P006Promise.all 内部异常未处理

  • 位置src/app/(dashboard)/parent/attendance/page.tsx:29-31grades/page.tsx:29-31
  • 问题ctx.dataScope.childrenIds.map((id) => getStudentAttendanceSummary(id)) 若任一查询抛错,整个页面 500。未做 try-catch 或 Promise.allSettled
  • 改进建议:使用 Promise.allSettled 并过滤 rejected或对单个子女查询失败显示局部错误状态

2.3 dashboard/page.tsx — 严重度:中

BUG-P007缺少 dataScope 空状态处理

  • 位置src/app/(dashboard)/parent/dashboard/page.tsx:7-15
  • 问题:未检查 ctx.dataScope.type === "children"childrenIds.length === 0,直接调用 getParentDashboardData(ctx.userId)。虽然 data-access 会返回空数组,但与 attendance/grades 页面的处理方式不一致
  • 改进建议:与 attendance/grades 页面统一,前置检查 dataScope

2.4 parent-dashboard.tsx — 严重度:中

BUG-P008使用 <a href> 而非 <Link>,丢失客户端导航

  • 位置src/modules/parent/components/parent-dashboard.tsx:30, 36
  • 问题<a href="/parent/grades"><a href="/announcements"> 使用原生 <a> 标签,导致整页刷新,丢失 Next.js 客户端路由优化
  • 违反规则Web Interface Guidelines — Navigation & State「Links use <a>/<Link> (Cmd/Ctrl+click, middle-click support)」Next.js 最佳实践
  • 改进建议import Link from "next/link",使用 <Link href="/parent/grades">

BUG-P009问候语使用 new Date().getHours() 存在时区风险

  • 位置src/modules/parent/components/parent-dashboard.tsx:12-16
  • 问题:服务端渲染时使用服务器时区计算问候语,与用户实际时区可能不符(例如部署在 UTC 服务器,北京时间早 8 点用户看到 "Good afternoon"
  • 违反规则Web Interface Guidelines — Locale & i18n「Dates/times: use Intl.DateTimeFormat not hardcoded formats」
  • 改进建议:使用 Intl.DateTimeFormat(undefined, { hour: "numeric", timeZone: ctx.timezone }) 或将问候语计算移至客户端组件

BUG-P010标题层级与间距与其他页面不一致

  • 位置src/modules/parent/components/parent-dashboard.tsx:22 vs attendance/page.tsx:16grades/page.tsx::16
  • 问题dashboard 使用 text-3xl + space-y-6attendance/grades 使用 text-2xl + space-y-8
  • 违反规则web-artifacts-builder 设计一致性原则
  • 改进建议:统一标题字号(建议 text-2xl)和间距(建议 space-y-6

2.5 child-card.tsx — 严重度:中

BUG-P011getInitials 函数重复定义

  • 位置src/modules/parent/components/child-card.tsx:9-12src/modules/parent/components/child-detail-header.tsx:9-12
  • 问题:两个文件定义了完全相同的 getInitials 函数
  • 违反规则DRY 原则
  • 改进建议:抽取到 src/modules/parent/lib/utils.tssrc/shared/lib/utils.ts

BUG-P012使用字符串拼接动态类名违反 Tailwind 规范

  • 位置src/modules/parent/components/child-card.tsx:57-60
  • 问题
    className={`text-lg font-semibold tabular-nums ${
      homeworkSummary.overdueCount > 0 ? "text-destructive" : ""
    }`}
    
    使用模板字符串拼接类名违反编码规范「Tailwind 规范:使用 cn() 工具函数管理条件类名」
  • 对比:同模块 child-homework-summary.tsx:72-75 正确使用了 cn()
  • 改进建议
    className={cn(
      "text-lg font-semibold tabular-nums",
      homeworkSummary.overdueCount > 0 && "text-destructive",
    )}
    

BUG-P013手动截断标题应使用 Tailwind truncate/line-clamp

  • 位置src/modules/parent/components/child-card.tsx:81-83
  • 问题
    ({latestGrade.assignmentTitle.slice(0, 20)}
    {latestGrade.assignmentTitle.length > 20 ? "..." : ""})
    
    手动 slice + "..." 截断,违反 Web Interface Guidelines — Typography「 not ...
  • 改进建议:使用 <span className="truncate inline-block max-w-[200px]">{latestGrade.assignmentTitle}</span>
  • 位置src/modules/parent/components/child-card.tsx:21
  • 问题<Card className="hover:bg-muted/50 transition-colors cursor-pointer h-full">cursor-pointer 冗余(外层 <Link> 默认 pointer
  • 违反规则Web Interface Guidelines — Anti-patterns
  • 改进建议:移除 cursor-pointer
  • 位置src/modules/parent/components/child-card.tsx:20-87
  • 问题<Link> 包裹整个 Card屏幕阅读器会读出所有内部文本姓名、班级、数字、最新成绩缺少简洁的 aria-label
  • 违反规则Web Interface Guidelines — Accessibility「Icon-only buttons need aria-label」延伸到卡片导航
  • 改进建议<Link href={...} aria-label={查看 ${basicInfo.name ?? "子女"} 的详情}>
  • 位置src/modules/parent/components/child-card.tsx:20-21
  • 问题<Link> 包裹 Card但 Card 没有 focus-visible:ring-* 样式,键盘导航时无可见焦点
  • 违反规则Web Interface Guidelines — Focus States「Interactive elements need visible focus」
  • 改进建议:添加 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2

2.6 child-detail-header.tsx — 严重度:低

BUG-P017getInitials 重复(同 BUG-P011

  • 位置src/modules/parent/components/child-detail-header.tsx:9-12
  • 改进建议:见 BUG-P011

BUG-P018邮箱直接展示未做防爬处理

  • 位置src/modules/parent/components/child-detail-header.tsx:43
  • 问题<span>· {basicInfo.email}</span> 直接展示子女邮箱,无防爬/掩码处理
  • 改进建议:考虑隐私场景下掩码处理(如 j***@example.com),或仅对家长本人可见时展示完整

2.7 child-grade-summary.tsx — 严重度:中

BUG-P019"use client" 必要性可优化

  • 位置src/modules/parent/components/child-grade-summary.tsx:1
  • 问题:组件标记为 "use client",但实际仅 recharts 需要客户端。整个组件(包括数据预处理 chartData 计算)都被打包到客户端 bundle
  • 违反规则vercel-react-best-practicesbundle-dynamic-imports「Use next/dynamic for heavy components」
  • 改进建议:将图表部分抽取为独立的客户端组件 GradeTrendChart,父组件保持服务端组件,通过 next/dynamic 懒加载图表

BUG-P020latestGrade 取数组末尾,语义不明确

  • 位置src/modules/parent/components/child-grade-summary.tsx:32
  • 问题const latestGrade = grades.trend[grades.trend.length - 1] 假设 trend 是按时间升序排列,但类型定义 StudentDashboardGradeProps 未明确顺序
  • 对比child-card.tsx:17 使用 gradeTrend.recent[0] 取最新(假设 recent 是降序)
  • 改进建议:在 homework/types.tsStudentDashboardGradeProps 中补充 JSDoc 说明 trendrecent 的排序语义

BUG-P021chartData 在每次渲染时重新计算

  • 位置src/modules/parent/components/child-grade-summary.tsx:34-41
  • 问题const chartData = grades.trend.map(...) 每次渲染都重新 map未 memoize
  • 违反规则vercel-react-best-practicesrerender-memo「Extract expensive work into memoized components」
  • 改进建议const chartData = useMemo(() => grades.trend.map(...), [grades.trend])

BUG-P022tickFormatter 内联函数每次渲染创建新引用

  • 位置src/modules/parent/components/child-grade-summary.tsx:99-101
  • 问题tickFormatter={(value) => value.slice(0, 8) + (value.length > 8 ? "..." : "")} 内联箭头函数
  • 违反规则Web Interface Guidelines — Typography「 not ...」;vercel-react-best-practicesrerender-functional-setstate
  • 改进建议:抽取为模块级纯函数 const formatTick = (v: string) => v.slice(0, 8) + (v.length > 8 ? "…" : "")

BUG-P023使用 "..." 应为

  • 位置src/modules/parent/components/child-grade-summary.tsx:100
  • 问题value.length > 8 ? "..." : "" 使用三个英文句号,应为省略号字符
  • 违反规则Web Interface Guidelines — Typography「 not ...

2.8 child-homework-summary.tsx — 严重度:低

BUG-P024状态字符串硬编码应使用枚举/常量

  • 位置src/modules/parent/components/child-homework-summary.tsx:10-21
  • 问题getStatusVariantgetStatusLabel 使用硬编码字符串 "graded""submitted""in_progress" 比较
  • 改进建议:从 homework/types.ts 导入状态常量或联合类型,使用 switch + exhaustive check

BUG-P025getDueUrgency 在渲染期调用 new Date()

  • 位置src/modules/parent/components/child-homework-summary.tsx:23-31, 95
  • 问题:每次 map 迭代都调用 new Date() 创建新日期对象,虽然性能影响小,但语义上应在外层计算一次 now
  • 改进建议:在组件顶部 const now = new Date() 一次,传入 getDueUrgency(a.dueAt, now)

2.9 child-schedule-card.tsx — 严重度:低

BUG-P026空状态高度与其他组件不一致

  • 位置src/modules/parent/components/child-schedule-card.tsx:31
  • 问题className="border-none h-60",而 child-grade-summary.tsx:57 使用 h-60child-homework-summary.tsx:87 使用 h-40
  • 改进建议:统一空状态高度(建议 h-48

2.10 data-access.ts — 严重度:中

BUG-P027toWeekday 使用 as 类型断言

  • 位置src/modules/parent/data-access.ts:28-31
  • 问题
    const toWeekday = (d: Date): 1 | 2 | 3 | 4 | 5 | 6 | 7 => {
      const day = d.getDay()
      return (day === 0 ? 7 : day) as 1 | 2 | 3 | 4 | 5 | 6 | 7
    }
    
    使用 as 类型断言,违反编码规范「禁止 as 断言(除非从 unknown 转换)」
  • 改进建议:使用类型守卫
    const toWeekday = (d: Date): 1 | 2 | 3 | 4 | 5 | 6 | 7 => {
      const day = d.getDay()
      const weekday = day === 0 ? 7 : day
      if (weekday < 1 || weekday > 7) throw new Error(`Invalid weekday: ${weekday}`)
      return weekday
    }
    

BUG-P028getChildBasicInfo 串行查询瀑布(架构图已标注 P2

  • 位置src/modules/parent/data-access.ts:58-117
  • 问题4 个串行 DB 查询users → grades → classEnrollments → classes。其中 grades 和 classEnrollments 互相独立,可并行
  • 违反规则vercel-react-best-practicesasync-parallel「Use Promise.all() for independent operations」
  • 架构图标注004 文档 2.19 节「⚠️ P2getChildBasicInfo 多次串行查询,可优化为 join」
  • 改进建议
    // grades 和 classEnrollments 并行
    const [gradeRow, enrollment] = await Promise.all([
      student.gradeId
        ? db.select({ name: grades.name }).from(grades).where(eq(grades.id, student.gradeId)).limit(1)
        : Promise.resolve([]),
      db.select({ classId: classEnrollments.classId, status: classEnrollments.status })
        .from(classEnrollments)
        .where(and(eq(classEnrollments.studentId, studentId), eq(classEnrollments.status, "active")))
        .orderBy(asc(classEnrollments.createdAt))
        .limit(1),
    ])
    
    或重构为单次 JOIN 查询

BUG-P029getChildBasicInfo 返回类型未显式标注

  • 位置src/modules/parent/data-access.ts:58
  • 问题export const getChildBasicInfo = cache(async (studentId: string, relation: string | null = null) => { 未显式标注返回类型
  • 违反规则:编码规范「函数返回值必须显式标注,特别是 Promise<T>
  • 改进建议:定义 ChildBasicInfo 返回类型并显式标注(types.ts 中已有 ChildBasicInfo 类型,可直接使用)

BUG-P030buildHomeworkSummary[...assignments].sort() 不必要的拷贝

  • 位置src/modules/parent/data-access.ts:150-156
  • 问题[...assignments].sort(...) 创建数组副本再排序。assignments 来自 getStudentHomeworkAssignments 返回的新数组,无需再拷贝
  • 改进建议:直接 assignments.sort(...) 或使用 toSorted()ES2023

2.11 types.ts — 严重度:低

BUG-P031类型缺少 JSDoc 文档注释

  • 位置src/modules/parent/types.ts 全文件
  • 问题:所有类型(ParentChildRelationChildBasicInfoChildScheduleItemChildHomeworkSummaryChildDashboardDataParentDashboardData)均无 JSDoc
  • 违反规则:编码规范 5.4「必须编写 JSDoc」
  • 改进建议:为每个类型补充 JSDoc说明用途、字段语义

BUG-P032ChildHomeworkSummary 与组件同名类型冲突风险

  • 位置src/modules/parent/types.ts:37child-homework-summary.tsx:33
  • 问题:类型名 ChildHomeworkSummary 与组件名 ChildHomeworkSummary 完全相同,在 child-homework-summary.tsx 中同时 import 两者会造成命名冲突
    import type { ChildHomeworkSummary } from "@/modules/parent/types"  // 类型
    export function ChildHomeworkSummary({ summary }: { summary: ChildHomeworkSummary }) // 组件
    
    当前依赖 TypeScript 类型与值的命名空间分离才不冲突,但可读性差
  • 改进建议:类型重命名为 ChildHomeworkSummaryData 或组件重命名为 ChildHomeworkSummaryCard

三、React 性能优化(应用 vercel-react-best-practices 技能)

PERF-P01getChildBasicInfo 串行查询瀑布(同 BUG-P028

  • 违反规则async-parallel — Use Promise.all() for independent operations
  • 改进建议:见 BUG-P028

PERF-P02chartData 未 memoize同 BUG-P021

  • 违反规则rerender-memo — Extract expensive work into memoized components
  • 改进建议:见 BUG-P021

PERF-P03child-grade-summary.tsx 整体客户端化bundle 体积大

  • 位置src/modules/parent/components/child-grade-summary.tsx:1
  • 问题"use client" 导致 recharts~100KB整体进入客户端 bundle但组件大部分逻辑数据预处理、布局可在服务端完成
  • 违反规则bundle-dynamic-imports — Use next/dynamic for heavy components
  • 改进建议
    // child-grade-summary.tsx (Server Component)
    import dynamic from "next/dynamic"
    const GradeTrendChart = dynamic(() => import("./grade-trend-chart").then(m => m.GradeTrendChart))
    // 仅图表部分客户端化
    

PERF-P04getParentDashboardData 内部 Promise.all 已正确并行化

  • 位置src/modules/parent/data-access.ts:225-227
  • 说明 已正确使用 Promise.all 并行获取所有子女数据,符合 async-parallel 规范

PERF-P05getChildDashboardData 内部 Promise.all 已正确并行化

  • 位置src/modules/parent/data-access.ts:190-196
  • 说明 已正确使用 Promise.all 并行获取 enrolledClasses/schedule/assignments/gradeTrend/gradeSummary

PERF-P06cache() 已正确包裹 data-access 函数

  • 位置src/modules/parent/data-access.ts:33, 58, 185, 209
  • 说明 所有 data-access 函数均使用 React cache() 包裹,符合 server-cache-react 规范,实现单次请求内去重

PERF-P07parent-dashboard.tsxnew Date() 在服务端执行无 hydration 风险

  • 位置src/modules/parent/components/parent-dashboard.tsx:12
  • 说明 组件为 Server Componentnew Date() 仅在服务端执行一次,无 hydration mismatch 风险(但有时区问题,见 BUG-P009

四、Web 界面规范审查(应用 web-design-guidelines 技能)

UI-P01parent-dashboard.tsx

src/modules/parent/components/parent-dashboard.tsx:30 - <a href> → use <Link> for client-side nav
src/modules/parent/components/parent-dashboard.tsx:36 - <a href> → use <Link> for client-side nav
src/modules/parent/components/parent-dashboard.tsx:12 - new Date() server-side, timezone mismatch risk
src/modules/parent/components/parent-dashboard.tsx:22 - title size inconsistent (text-3xl vs text-2xl in other pages)

UI-P02child-card.tsx

src/modules/parent/components/child-card.tsx:20 - Link wrapping Card lacks aria-label
src/modules/parent/components/child-card.tsx:21 - cursor-pointer redundant on Link
src/modules/parent/components/child-card.tsx:21 - missing focus-visible:ring-* for keyboard nav
src/modules/parent/components/child-card.tsx:57 - string concatenation for className → use cn()
src/modules/parent/components/child-card.tsx:82 - "..." → "…"
src/modules/parent/components/child-card.tsx:82 - manual slice truncation → use truncate/line-clamp

UI-P03child-grade-summary.tsx

src/modules/parent/components/child-grade-summary.tsx:100 - "..." → "…"
src/modules/parent/components/child-grade-summary.tsx:99 - inline tickFormatter → hoist to module scope
src/modules/parent/components/child-grade-summary.tsx:142 - Link lacks query param for tab deep-linking

UI-P04child-homework-summary.tsx

src/modules/parent/components/child-homework-summary.tsx:98 - Link lacks query param for tab deep-linking
src/modules/parent/components/child-homework-summary.tsx:25 - new Date() in each map iteration → hoist to component scope

UI-P05child-detail-header.tsx

src/modules/parent/components/child-detail-header.tsx:43 - email displayed without masking (privacy)

UI-P06attendance/page.tsx & grades/page.tsx

src/app/(dashboard)/parent/attendance/page.tsx:14 - h-full flex-1 flex-col space-y-8 p-8 md:flex → inconsistent with dashboard/page.tsx (p-6 md:p-8)
src/app/(dashboard)/parent/grades/page.tsx:14 - same inconsistency as above

UI-P07空状态一致性

src/modules/parent/components/child-schedule-card.tsx:31 - EmptyState h-60
src/modules/parent/components/child-grade-summary.tsx:57 - EmptyState h-60
src/modules/parent/components/child-homework-summary.tsx:87 - EmptyState h-40
src/app/(dashboard)/parent/attendance/page.tsx:24 - EmptyState border-none shadow-none (no height)
→ unify EmptyState height and className

五、界面优化建议(应用 web-artifacts-builder 技能)

UIX-P01子女卡片网格响应式断点不足

  • 位置src/modules/parent/components/parent-dashboard.tsx:59
  • 问题grid-cols-1 md:grid-cols-2 lg:grid-cols-3 在 sm 屏幕下强制单列2 列布局在 sm640px下更合适
  • 改进建议grid-cols-1 sm:grid-cols-2 lg:grid-cols-3

UIX-P02详情页布局中等屏幕下右侧栏过窄

  • 位置src/modules/parent/components/child-detail-panel.tsx:12
  • 问题grid-cols-1 lg:grid-cols-3 在 md768-1024px下为单列lg:col-span-2 在 lg 下才生效md 下左侧内容占满,右侧课表也在下方
  • 改进建议:增加 md 断点 grid-cols-1 md:grid-cols-2 lg:grid-cols-3,左侧 md:col-span-1 lg:col-span-2

UIX-P03卡片内嵌套卡片视觉层级混乱

  • 位置src/modules/parent/components/child-card.tsx:43-73
  • 问题Card 内部 CardContent 中又使用 rounded-md border bg-card p-2 创建 3 个小卡片,与外层 Card 视觉层级冲突
  • 改进建议:内部小卡片改用 bg-muted/50 或移除 border弱化层级

UIX-P04作业摘要卡片缺少"查看全部"链接

  • 位置src/modules/parent/components/child-homework-summary.tsx:90-126
  • 问题:仅展示 recentAssignments(最多 5 条),无"查看全部作业"入口
  • 改进建议:底部添加 <Link href="/parent/children/{childId}?tab=homework">View all</Link>

UIX-P05成绩趋势图 X 轴标签截断后信息丢失

  • 位置src/modules/parent/components/child-grade-summary.tsx:94-102
  • 问题tickFormatter 截断为 8 字符 + "…",多个作业标题前 8 字符相同时无法区分
  • 改进建议X 轴改为日期(formatDate(submittedAt)),标题在 tooltip 中完整展示

UIX-P06仪表盘快捷入口仅 2 个,可扩展

  • 位置src/modules/parent/components/parent-dashboard.tsx:28-41
  • 问题:仅有 Grades 和 Announcements 两个快捷按钮,缺少 Attendance、Schedule 等常用入口
  • 改进建议:增加 Attendance 快捷入口,或改为下拉菜单

六、架构文档同步问题

DOC-P01004 文档 parent 模块行数记录过期

  • 位置docs/architecture/004_architecture_impact_map.md:924
  • 问题:记录 data-access.ts | 234 | 子女关系 + 仪表盘数据聚合,实际 234 行 一致;但 components/* | 7 文件 实际为 7 个组件文件 一致
  • 说明:本节核查后无需更新(行数与文件数均一致)

DOC-P02004 文档未记录 children/[studentId]/page.tsx 的架构违规

  • 位置docs/architecture/004_architecture_impact_map.md 2.19 节
  • 问题:未在 parent 模块「已知问题」中记录 app/(dashboard)/parent/children/[studentId]/page.tsx 直接访问 DB 的违规BUG-P001
  • 改进建议:在 004 文档 2.19 节「已知问题」中补充:
    - ❌ P1`app/(dashboard)/parent/children/[studentId]/page.tsx` 直接访问 DB违反三层架构
    

DOC-P03005 JSON 中 parent 模块的 routes 节点需补充

  • 问题:若修复 BUG-P005抽取共享组件路由结构不变但需在 005 JSON 中记录 attendance/grades 页面的 fetcher 依赖关系
  • 改进建议:在 005_architecture_data.jsonmodules.parent.dependencies 中补充 attendancegrades 模块依赖

七、问题汇总统计

严重度 数量 问题编号
高(架构违规/安全) 6 BUG-P001, BUG-P002, BUG-P004, BUG-P005, BUG-P006, BUG-P028
中(规范违规/性能) 12 BUG-P003, BUG-P007, BUG-P008, BUG-P009, BUG-P010, BUG-P011, BUG-P012, BUG-P019, BUG-P020, BUG-P021, BUG-P027, BUG-P029
低(代码质量/UX 13 BUG-P013, BUG-P014, BUG-P015, BUG-P016, BUG-P017, BUG-P018, BUG-P022, BUG-P023, BUG-P024, BUG-P025, BUG-P026, BUG-P030, BUG-P031, BUG-P032
合计 31

按技能分类统计

技能 发现问题数 主要问题类型
项目规范核查 18 架构违规、代码重复、类型规范、Tailwind 规范
vercel-react-best-practices 7 串行查询瀑布、bundle 体积、memoize 缺失
web-design-guidelines 15 可访问性、焦点状态、排版、导航、空状态一致性
web-artifacts-builder 6 响应式断点、视觉层级、交互入口、图表可读性

八、修复优先级建议

P0立即修复 — 架构与安全)

  1. BUG-P001children/[studentId]/page.tsx 移除直接 DB 访问,下沉到 parent/data-access.ts
  2. BUG-P002:权限校验加 parentId 条件,防止信息泄露
  3. BUG-P005:抽取 ParentChildrenDataPage 共享组件,消除 attendance/grades 重复

P1短期修复 — 规范与性能)

  1. BUG-P008<a href> 改为 <Link>
  2. BUG-P012child-card.tsx 使用 cn() 替代字符串拼接
  3. BUG-P011:抽取 getInitials 到共享 utils
  4. BUG-P028getChildBasicInfo 并行化查询
  5. BUG-P019child-grade-summary.tsx 拆分服务端/客户端组件
  6. BUG-P027toWeekday 移除 as 断言
  7. BUG-P029getChildBasicInfo 显式标注返回类型

P2机会修复 — UX 与代码质量)

  1. BUG-P009:问候语时区处理
  2. BUG-P013:使用 truncate 替代手动截断
  3. BUG-P015, BUG-P016:卡片可访问性增强
  4. BUG-P023...
  5. BUG-P031:补充类型 JSDoc
  6. UIX-P01~P06:界面优化项

九、标杆实践(建议保留)

实践 位置 说明
cache() 包裹 data-access data-access.ts:33, 58, 185, 209 符合 server-cache-react,单次请求去重
Promise.all 并行获取子女数据 data-access.ts:190-196, 225-227 符合 async-parallel,消除瀑布
Server Component 默认 7/8 组件为 Server Component child-grade-summary.tsx 因 recharts 标记 client
import type 正确使用 所有类型导入均使用 import type 符合编码规范 4.2.6
server-only 标注 data-access.ts:1 防止 data-access 被客户端误引入
空状态处理完整 所有页面均使用 EmptyState 组件 UX 一致性良好

说明:本报告基于 2026-06-18 代码状态生成。修复后需同步更新 docs/architecture/004_architecture_impact_map.md 2.19 节与 005_architecture_data.json 的 parent 模块节点。