16 KiB
组件化重构专项设计文档
| 字段 | 值 |
|---|---|
| 文档版本 | v1 |
| 创建日期 | 2026-07-06 |
| 作者 | Trae 协作生成 |
| 状态 | 待用户审查 |
| 范围 | 巨型文件拆分(9 个)+ 重复组件收敛(49 个)+ 共享底座补全 |
| 策略 | 混合:先收敛底座,再按模块垂直闭环(3 批风险分级) |
1. 背景与现状
1.1 巨型文件清单(9 个,>400 行)
基于 2026-07-06 实际扫描:
| # | 路径 | 行数 | 模块 |
|---|---|---|---|
| 1 | src/modules/textbooks/components/textbook-reader.tsx |
>450 | textbooks |
| 2 | src/modules/lesson-preparation/components/lesson-plan-editor.tsx |
>450 | lesson-preparation |
| 3 | src/modules/grades/components/batch-grade-entry.tsx |
>450 | grades |
| 4 | src/modules/grades/components/grade-record-list.tsx |
>450 | grades |
| 5 | src/modules/homework/components/homework-take-view.tsx |
428 | homework |
| 6 | src/modules/homework/components/homework-grading-view.tsx |
>450 | homework |
| 7 | src/modules/exams/components/exam-assembly.tsx |
>450 | exams |
| 8 | src/modules/ai/components/ai-chat-panel.tsx |
417 | ai |
| 9 | src/modules/textbooks/components/knowledge-graph-inner.tsx |
411 | textbooks |
已瘦身的 5 个文件(不在本次范围内):exam-rich-form.tsx (384)、message-list.tsx (396)、selection-toolbar.tsx (366)、error-book-detail-dialog.tsx (336)、admin-users-view.tsx (332)。
1.2 重复组件清单(49 个)
| 类型 | 数量 | 示例路径 |
|---|---|---|
*-filters.tsx |
20 | grades/components/grade-filters.tsx 等 |
*-stats-cards.tsx / *-stats-card.tsx |
8 | attendance/components/attendance-stats-cards.tsx 等 |
*-skeleton.tsx |
8 | ai/components/ai-skeleton.tsx 等 |
*-error-boundary.tsx |
8 | audit/components/audit-error-boundary.tsx 等 |
*-dashboard.tsx |
5 | dashboard/components/admin-dashboard/admin-dashboard.tsx 等 |
已知冲突:
textbooks/components/section-error-boundary.tsx与shared/components/section-error-boundary.tsx同名attendance/components/attendance-stats-card.tsx(单数)与attendance-stats-cards.tsx(复数)并存
1.3 共享底座现状(关键发现)
| 底座 | 状态 | 路径 | 当前接口 |
|---|---|---|---|
FilterBar |
已存在 | shared/components/ui/filter-bar.tsx (124 行) |
children-based 组合式:FilterBar + FilterSearchInput + FilterResetButton |
StatCard |
已存在 | shared/components/ui/stat-card.tsx (95 行) |
单卡片,无 grid 容器 |
Skeleton |
已存在 | shared/components/ui/skeleton.tsx (15 行) |
仅原语,无 variant |
ErrorBoundary |
缺失 | — | 有 3 个分散替代件:section-error-boundary.tsx (148)、route-error.tsx (44)、widget-boundary.tsx (155) |
1.4 架构文档缺口
docs/architecture/005_architecture_data.json 的 lastUpdate 仅记录缓存策略重构(2026-07-05),未记录 filter-bar/stat-card/widget-boundary 等底座组件的存在,违反项目规则"新增组件必须同步 004/005"。
2. 目标与非目标
2.1 目标
- 补全底座:新增
ui/error-boundary.tsx基础组件 + 收敛 3 个分散替代件为 preset - 扩展底座:新增
StatsGrid容器(基于StatCard)、SkeletonCardvariant(基于Skeleton) - 拆分巨型文件:9 个 >400 行组件全部拆为容器+子组件,目标 ≤500 行
- 迁移重复组件:49 个重复组件迁移到底座,删除旧实现(不留 backwards-compat shim)
- 同步架构文档:004 / 005 / known-issues.md 全量更新
2.2 非目标(YAGNI)
shared/db/schema.ts(1111 行) 拆分 — 属于解耦专项exams/actions.ts(633 行) 拆分 — 属于解耦专项- 各模块
data-access.ts拆分 — 属于解耦专项 proctoring/exam-mode-config.tsx死代码清理 — 单独任务useDialogState批量接入 — V5 状态专项收尾- 跨模块直接 DB 查询扫描 — 解耦专项遗留
- FilterBar 接口改为配置驱动 — 当前 children-based 组合式更符合 React 模式,保持不变
- 单元测试编写 — 项目无单测覆盖,本次不引入
3. 架构设计
3.1 Phase 1:底座收敛与补全
3.1.1 ErrorBoundary 收敛方案
新增 src/shared/components/ui/error-boundary.tsx:
interface ErrorBoundaryProps {
fallback: React.ReactNode | ((error: Error, reset: () => void) => React.ReactNode);
onError?: (error: Error, info: React.ErrorInfo) => void;
children: React.ReactNode;
}
export class ErrorBoundary extends React.Component<ErrorBoundaryProps, { hasError: boolean; error: Error | null }> {
// 标准 React ErrorBoundary 实现
}
收敛 3 个替代件为 preset(保留外部 API,内部改用 <ErrorBoundary>):
| 现有文件 | 收敛后 | 位置 |
|---|---|---|
shared/components/section-error-boundary.tsx |
SectionErrorBoundary preset |
保留原路径,内部改为 <ErrorBoundary fallback={...}> 包装 |
shared/components/route-error.tsx |
RouteErrorBoundary preset |
保留原路径 |
shared/components/widget-boundary.tsx |
WidgetBoundary preset |
保留原路径(含 Suspense + Skeleton 三合一) |
模块层 8 个 *-error-boundary.tsx:删除,改为直接使用 SectionErrorBoundary preset。
3.1.2 StatsGrid 新增方案
新增 src/shared/components/ui/stats-grid.tsx:
interface StatItem {
label: string;
value: string | number;
trend?: { direction: 'up' | 'down' | 'flat'; delta: string };
icon?: React.ComponentType<{ className?: string }>;
href?: string;
}
interface StatsGridProps {
items: StatItem[];
columns?: 2 | 3 | 4; // 默认响应式
isLoading?: boolean;
}
内部用 StatCard 渲染,isLoading 时渲染 StatCardSkeleton。
3.1.3 SkeletonCard variant 方案
扩展 src/shared/components/ui/skeleton.tsx(15 行 → ~80 行):
interface SkeletonCardProps {
variant: 'table' | 'list' | 'chart' | 'form' | 'stats-grid';
rows?: number;
}
export function SkeletonCard({ variant, rows = 5 }: SkeletonCardProps) {
// 按 variant 渲染不同骨架布局
}
export function Skeleton(props: React.HTMLAttributes<HTMLDivElement>) {
// 保留原语
}
3.1.4 FilterBar 与 StatCard
FilterBar:保持现状,不修改接口。模块层*-filters.tsx改为<FilterBar><FilterSearchInput .../><select .../></FilterBar>组合实现StatCard:保持现状,作为StatsGrid内部使用
3.1.5 Phase 1 交付物
| 文件 | 操作 |
|---|---|
shared/components/ui/error-boundary.tsx |
新增 |
shared/components/ui/stats-grid.tsx |
新增 |
shared/components/ui/skeleton.tsx |
扩展(新增 SkeletonCard) |
shared/components/ui/filter-bar.tsx |
不修改 |
shared/components/ui/stat-card.tsx |
不修改 |
shared/components/section-error-boundary.tsx |
内部改用 ErrorBoundary |
shared/components/route-error.tsx |
内部改用 ErrorBoundary |
shared/components/widget-boundary.tsx |
内部改用 ErrorBoundary |
shared/components/index.ts |
新增 re-export |
docs/architecture/004 / 005 |
同步底座节点 |
3.2 Phase 2:模块垂直闭环
3.2.1 批次划分
| 批次 | 模块 | 巨型文件 | 重复组件 | 风险 |
|---|---|---|---|---|
| 第 1 批 | grades | batch-grade-entry.tsx、grade-record-list.tsx |
grade-stats-card、grade-filters、grade-query-filters、analytics-filters |
低 |
| attendance | — | attendance-stats-cards、attendance-stats-card(单数,重复)、attendance-filters |
低 | |
| error-book | — | error-book-stats-cards、error-book-filters、analytics-stats-cards |
低 | |
| elective | — | elective-stats-cards、elective-filters |
低 | |
| 第 2 批 | homework | homework-take-view.tsx (428)、homework-grading-view.tsx |
assignment-filters |
中 |
| exams | exam-assembly.tsx |
exam-filters |
中 | |
| textbooks | textbook-reader.tsx、knowledge-graph-inner.tsx (411) |
textbook-filters、section-error-boundary.tsx(同名冲突) |
中 | |
| 第 3 批 | lesson-preparation | lesson-plan-editor.tsx |
lesson-plan-filters、lesson-plan-skeleton、lesson-plan-error-boundary |
高 |
| ai | ai-chat-panel.tsx (417) |
ai-skeleton、ai-error-boundary |
高 | |
| dashboard | — | dashboard-loading-skeleton、4 个角色 dashboard 子组件(仅抽象布局壳) |
高 |
3.2.2 单模块闭环流程(6 步)
1. 模块审计 → Read 全部目标文件 + 重复组件,记录依赖与外部 API
2. 拆分巨型文件 → 拆为 容器 + 子组件(保持外部 API 不变)
3. 迁移到底座 → *-filters → FilterBar、*-stats-cards → StatsGrid、
*-skeleton → SkeletonCard、*-error-boundary → SectionErrorBoundary
4. 删除旧组件 → 直接删除,不留 backwards-compat shim
5. 验证 → tsc --noEmit + npm run lint + 手动回归
6. 文档同步 → 004 / 005 / known-issues.md
3.2.3 跨批次依赖规则
- Phase 1 完成 → 启动第 1 批
- 第 1 批完成 → 评估底座接口是否需调整(允许返回 Phase 1 调整)
- 第 2 批启动前 → 第 1 批验证通过
- 第 3 批启动前 → 第 2 批验证通过(高风险模块需底座稳定)
3.2.4 dashboard 4 角色抽象范围
仅抽象布局壳(<DashboardShell> 含 PageHeader + StatsGrid 区 + 内容区),各角色业务逻辑保持独立。不强制 4 个 dashboard 使用相同内容区。
4. 验收标准
4.1 代码验收(硬性门禁)
| 检查项 | 命令 | 通过标准 |
|---|---|---|
| TypeScript | npx tsc --noEmit |
零错误 |
| ESLint | npm run lint |
零新增错误(pre-existing warnings 不计入) |
| 文件行数 | Read 确认 | 9 个巨型文件拆分后 ≤500 行 |
| 重复组件删除 | Grep 旧组件名 | 全局无残留引用 |
| 外部 API 不变 | Grep 模块导出名 | 容器组件对外 API 签名不变 |
| 设计令牌合规 | Grep #hex、'Inter'、w-[ |
零违规(豁免场景除外) |
4.2 行为验收(手动回归)
| 模块类型 | 验收方式 |
|---|---|
| 列表/筛选页 | 进入页面 → 应用筛选 → 重置 → 翻页 → 数据正确 |
| 表单页 | 打开 Dialog → 填写 → 提交 → 列表刷新 |
| 编辑器 | 进入 → 编辑内容 → 保存 → 重新加载内容存在 |
| dashboard | 4 角色分别登录 → 卡片渲染 → 数据正确 |
4.3 文档同步
004_architecture_impact_map.md:模块章节组件清单 + shared/components 章节 + 文末"组件化重构专项"章节005_architecture_data.json:modules.*.exports+sharedComponents+lastUpdate+dependencyMatrixknown-issues.md:每批新增章节,速查手册风格记录底座使用规则与拆分模式
4.4 Commit 规范
每批一个 squash commit:
refactor(grades,attendance,error-book,elective): 组件化重构第 1 批
- 新增 ErrorBoundary/StatsGrid/SkeletonCard 底座
- 拆分 grades/batch-grade-entry.tsx (XXX → YYY 行)
- 删除 12 个重复组件,迁移到底座
- 同步 004/005 架构图,known-issues 新增章节
验收:tsc + lint 零错误,手动回归 4 模块页面通过
5. 风险与缓解
| 风险 | 缓解 |
|---|---|
| 底座接口设计不准 → 返工 | 第 1 批低风险模块验证后允许调整底座 |
| 巨型文件拆分破坏外部 API | 容器保持外部 API 不变,仅内部拆子组件 |
| 删除旧组件后引用未清理 | tsc + lint + grep 三重校验 |
textbooks 同名 section-error-boundary.tsx 冲突 |
模块层版本删除,改用 shared 层 |
| 富文本/编辑器拆分难度高 | textbooks 按 003_ui_refactoring_plan.md 已有方案执行 |
| dashboard 4 角色抽象范围蔓延 | 仅抽象布局壳,业务逻辑保持独立 |
6. 失败回滚策略
- 模块闭环中暴露底座接口问题 → 停止该模块,回 Phase 1 调整底座,已完成模块不回滚
- 模块闭环中暴露业务逻辑问题(非组件化引起)→ 不在本次范围,记录到 known-issues,组件化继续推进
7. 完成定义(Definition of Done)
- Phase 1 完成:ErrorBoundary/StatsGrid/SkeletonCard 落地,3 个替代件收敛为 preset
- 第 1 批完成:grades/attendance/error-book/elective 闭环
- 第 2 批完成:homework/exams/textbooks 闭环
- 第 3 批完成:lesson-preparation/ai/dashboard 闭环
- 9 个巨型文件全部 ≤500 行
- 49 个重复组件全部删除并迁移到底座
- 004 / 005 / known-issues 同步更新
- 3 个批次 commit 全部提交
8. 附录:完整重复组件清单
8.1 *-filters.tsx(20 个)
shared/components/question/question-bank-filters.tsx
modules/grades/components/grade-query-filters.tsx
modules/grades/components/grade-filters.tsx
modules/grades/components/analytics-filters.tsx
modules/classes/components/students-filters.tsx
modules/classes/components/schedule-filters.tsx
modules/attendance/components/attendance-filters.tsx
modules/error-book/components/error-book-filters.tsx
modules/exams/components/exam-filters.tsx
modules/audit/components/data-change-log-filters.tsx
modules/audit/components/login-log-filters.tsx
modules/audit/components/audit-log-filters.tsx
modules/lesson-preparation/components/lesson-plan-filters.tsx
modules/homework/components/assignment-filters.tsx
modules/questions/components/question-filters.tsx
modules/student/components/course-filters.tsx
modules/student/components/student-schedule-filters.tsx
modules/school/components/grade-insights-filters.tsx
modules/textbooks/components/textbook-filters.tsx
modules/elective/components/elective-filters.tsx
8.2 *-stats-cards.tsx / *-stats-card.tsx(8 个)
modules/error-book/components/analytics-stats-cards.tsx
modules/error-book/components/error-book-stats-cards.tsx
modules/attendance/components/attendance-stats-cards.tsx
modules/attendance/components/attendance-stats-card.tsx ← 单数,与复数重复
modules/elective/components/elective-stats-cards.tsx
modules/adaptive-practice/components/practice-overview-stats-cards.tsx
modules/adaptive-practice/components/practice-stats-cards.tsx
modules/grades/components/grade-stats-card.tsx
8.3 *-skeleton.tsx(8 个)
modules/messaging/components/message-list-skeleton.tsx
modules/announcements/components/announcement-list-skeleton.tsx
modules/audit/components/audit-log-table-skeleton.tsx
modules/classes/components/class-skeleton.tsx
modules/ai/components/ai-skeleton.tsx
modules/school/components/school-skeleton.tsx
modules/dashboard/components/dashboard-loading-skeleton.tsx
modules/lesson-preparation/components/lesson-plan-skeleton.tsx
8.4 ErrorBoundary 相关文件(10 个)
shared 层 preset(3 个,保留并内部收敛到 <ErrorBoundary>)
shared/components/section-error-boundary.tsx
shared/components/route-error.tsx
shared/components/widget-boundary.tsx
模块层待删除(7 个,改为直接使用 SectionErrorBoundary preset)
modules/audit/components/audit-error-boundary.tsx
modules/school/components/school-error-boundary.tsx
modules/classes/components/class-error-boundary.tsx
modules/textbooks/components/section-error-boundary.tsx ← 与 shared 同名,删除
modules/settings/components/settings-section-error-boundary.tsx
modules/ai/components/ai-error-boundary.tsx
modules/lesson-preparation/components/lesson-plan-error-boundary.tsx
8.5 *-dashboard.tsx(5 个)
modules/ai/components/ai-usage-dashboard.tsx
modules/dashboard/components/parent-dashboard/parent-dashboard.tsx
modules/dashboard/components/admin-dashboard/admin-dashboard.tsx
modules/proctoring/components/proctoring-dashboard.tsx
modules/exams/components/exam-analytics-dashboard.tsx