- 全项目逐文件审查: 4 份审计报告(shared/core-business/management/new-modules) - 重写 004 架构影响地图: 图优先 + 模块依赖图 + 数据流 + 调用链 + 问题分级 - 更新 005 结构化数据: 新增 architectureOverview/moduleDependencyGraph/knownIssues/dbTables 节点 - 更新 006 功能清单: 143 项功能标注实现状态, P0 覆盖率 80%->92% - 更新 007 差距审计: v2->v3, P0 完成 69%->84%, 新增架构技术债章节 - 更新 001 项目概览: 6 角色/54 权限/26 模块/54 表 - 新增 docs/README.md 文档索引 - 归档 11 份过时文档(002x2/003/designx8) 标注 - 更新 work_log
11 KiB
⚠️ 已归档文档 本文档是 2026-06-16 架构审核后的 UI 代码结构重构计划(评分 7.2/10),描述的是待修复的质量问题与重构方案。 当前重构计划已执行完毕,组件拆分、复用抽象、测试保障等改进已落地,最新架构状态详见 004 架构影响地图。 保留用于历史参考,不再维护。
UI 代码结构重构计划
基于 2026-06-16 架构审核,整体评分 7.2/10 目标:补齐工程细节短板,达到企业级标准(8.5+)
审核评分总览
| 维度 | 评分 | 状态 |
|---|---|---|
| 目录结构分层 | 8/10 | 良好 |
| 组件设计 | 6.5/10 | 需重构 |
| 复用与抽象 | 6/10 | 需重构 |
| 样式组织 | 8.5/10 | 优秀 |
| 可维护性 | 7/10 | 一般 |
| 性能相关 | 7/10 | 一般 |
| 测试与质量保障 | 5.5/10 | 需重构 |
| 安全性 | 7.5/10 | 良好 |
P0 — 紧急(阻塞级质量问题)
P0-1 拆分巨型组件
问题: exam-form.tsx(1623 行)、textbook-reader.tsx(744 行)严重违反单一职责,难以维护和测试。
exam-form.tsx 拆分方案
src/modules/exams/components/
exam-form.tsx ← 保留为容器组件(~200 行),组合子组件
exam-basic-info-form.tsx ← 考试基本信息(名称、科目、时间)
exam-ai-generator.tsx ← AI 生成试卷功能
exam-structure-editor.tsx ← 试卷结构编辑
exam-question-selector.tsx ← 题目选择器
exam-preview-panel.tsx ← 试卷预览面板
exam-form-actions.tsx ← 表单操作按钮组
textbook-reader.tsx 拆分方案
src/modules/textbooks/components/
textbook-reader.tsx ← 保留为容器组件(~150 行)
textbook-content-panel.tsx ← 内容阅读面板(Markdown 渲染)
knowledge-point-list.tsx ← 知识点列表
knowledge-graph.tsx ← 知识图谱可视化
knowledge-point-dialogs.tsx ← 创建/编辑知识点 Dialog
textbook-editor-panel.tsx ← 编辑模式面板
use-text-selection.ts ← 文本选择逻辑 Hook
use-knowledge-point-actions.ts ← 知识点操作逻辑 Hook
验收标准:
- 单文件不超过 300 行
- 每个子组件职责单一,可独立测试
- 容器组件仅负责组合和状态分发
P0-2 修复无障碍(a11y)缺陷
问题: 全项目仅 7 处 aria-label,但 180 处 onClick;存在 <div onClick> 非语义化标签。
修复清单
-
图标按钮补 aria-label
- 搜索所有
<Button.*size="icon"或仅含图标的按钮,补充aria-label - 涉及文件:exam-actions.tsx、question-columns.tsx、exam-columns.tsx、breadcrumb.tsx 等
- 搜索所有
-
替换
<div onClick>为语义化标签- textbook-reader.tsx:434 — 知识点卡片 →
<button> - 搜索所有
<div onClick/<span onClick模式,逐一替换
- textbook-reader.tsx:434 — 知识点卡片 →
-
表单控件补 label
- 确认所有
<Input>/<Select>有关联<Label htmlFor>或aria-label
- 确认所有
-
添加 skip-link
- Root Layout 或 Dashboard Layout 添加:
<a href="#main-content" className="sr-only focus:not-sr-only ..."> Skip to main content </a> <main>添加id="main-content"
- Root Layout 或 Dashboard Layout 添加:
验收标准:
- axe-core 扫描零严重违规
- 所有图标按钮有
aria-label - 无
<div onClick>/<span onClick>模式
P1 — 重要(架构级改进)
P1-1 创建自定义 Hooks 层
问题: 整个项目零自定义 Hook,逻辑耦合在组件中。
通用 Hooks(src/shared/hooks/)
src/shared/hooks/
use-action-with-toast.ts ← Server Action + toast 反馈
use-async-action.ts ← 异步操作 loading/error 状态
use-debounce.ts ← 防抖
use-media-query.ts ← 响应式断点
use-local-storage.ts ← 本地存储
示例实现:
// src/shared/hooks/use-action-with-toast.ts
"use client"
import { useTransition } from "react"
import { toast } from "sonner"
import type { ActionState } from "@/shared/types/action-state"
export function useActionWithToast<T>() {
const [isPending, startTransition] = useTransition()
const execute = async (action: () => Promise<ActionState<T>>) => {
startTransition(async () => {
const result = await action()
if (result.success) {
toast.success(result.message || "操作成功")
} else {
toast.error(result.message || "操作失败")
}
})
}
return { isPending, execute }
}
模块级 Hooks(src/modules/*/hooks/)
src/modules/exams/hooks/
use-exam-form.ts ← 考试表单状态管理
use-exam-filters.ts ← 考试筛选逻辑
src/modules/textbooks/hooks/
use-text-selection.ts ← 文本选择 + 知识点创建
use-knowledge-point-actions.ts ← 知识点 CRUD 操作
src/modules/homework/hooks/
use-homework-submission.ts ← 作业提交逻辑
验收标准:
- 组件中无超过 3 个
useState的逻辑块(应提取为 Hook) - 通用 Hook 有单元测试
P1-2 添加动画降级(prefers-reduced-motion)
问题: 全项目零处 prefers-reduced-motion 引用,不符合 WCAG 2.1。
方案
- globals.css 添加全局降级
@layer base {
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
}
- tailwindcss-animate 适配
确认 tailwindcss-animate 生成的动画类在 prefers-reduced-motion: reduce 下被正确降级。如不支持,在 globals.css 中补充:
@media (prefers-reduced-motion: reduce) {
.animate-accordion-down,
.animate-accordion-up {
animation: none !important;
}
}
验收标准:
- 操作系统开启"减少动态效果"后,页面无动画
- Lighthouse Accessibility 评分 ≥ 90
P1-3 引入 Next.js 图片与字体优化
next/image
- 搜索所有
<img>标签,替换为next/image - 配置
next.config.ts的images.remotePatterns(如有外部图片源)
next/font
// src/app/layout.tsx
import { Inter } from "next/font/google"
const inter = Inter({
subsets: ["latin"],
display: "swap",
variable: "--font-inter",
})
验收标准:
- Lighthouse Performance 无 "Properly size images" 警告
- 无 "Eliminate render-blocking resources" 字体相关警告
P2 — 改进(质量提升)
P2-1 补充组件单元测试
问题: 零组件测试、零 Hook 测试、零工具函数测试。
测试目录结构(co-location 模式)
src/modules/exams/components/
exam-card.tsx
exam-card.test.tsx ← 新增
src/shared/hooks/
use-action-with-toast.ts
use-action-with-toast.test.ts ← 新增
src/shared/lib/
utils.ts
utils.test.ts ← 新增
优先级
- 高:
useActionWithToast、cn()、formatDate() - 高:ExamCard、QuestionColumns、HomeworkAssignmentForm
- 中:各模块 data-access.ts(mock DB 测试)
- 低:UI 基础组件(shadcn/ui 已有上游测试)
配置更新
// vitest.config.ts 添加
test: {
include: ["src/**/*.test.{ts,tsx}"],
}
验收标准:
- 关键路径覆盖率 > 80%
- CI 中
npm run test:integration包含单元测试
P2-2 统一 Tailwind v4 配置
问题: tailwind.config.ts(v3 风格)与 globals.css 的 @theme inline(v4 风格)并存。
方案
- 将
tailwind.config.ts中的theme.extend迁移至globals.css的@theme inline块 - 删除
tailwind.config.ts中与 CSS 重复的定义 - 保留
tailwind.config.ts仅用于plugins和content配置(如 v4 仍需要)
验收标准:
- 无重复的主题定义
npm run build无 Tailwind deprecation 警告
P2-3 清理 Mock 数据
问题: src/modules/exams/mock-data.ts 和 src/modules/questions/mock-data.ts 残留在生产代码中。
方案
src/mocks/ ← 新增
exam-data.ts ← 从 modules/exams/mock-data.ts 迁移
question-data.ts ← 从 modules/questions/mock-data.ts 迁移
tests/mocks/ ← 或放测试目录
exam-factories.ts ← 使用 @faker-js/faker 生成
question-factories.ts
- 删除
src/modules/*/mock-data.ts - 确认无生产代码引用 mock 数据
验收标准:
src/modules/下无mock-data.ts- 生产构建不包含 mock 数据
P3 — 优化(锦上添花)
P3-1 i18n 文案提取
问题: 硬编码中文字案散布在组件中。
方案
- 引入
next-intl或轻量 i18n 方案 - 提取所有用户可见文案到
src/shared/i18n/zh-CN.json - 代码中通过
t("knowledgePoint.created")引用
P3-2 替换原生 confirm/alert
问题: textbook-reader.tsx 使用 confirm(),与项目 UI 风格不一致。
方案
- 创建
useConfirmDialogHook,封装 AlertDialog - 全局搜索
confirm(和alert(,逐一替换
P3-3 react-markdown 安全加固
问题: textbook-reader.tsx 渲染用户内容,remarkGfm 启用 HTML 后需防 XSS。
方案
import rehypeSanitize from "rehype-sanitize"
<ReactMarkdown
remarkPlugins={[remarkGfm, remarkBreaks]}
rehypePlugins={[rehypeSanitize]} ← 新增
>
P3-4 formatDate 国际化
问题: utils.ts 中 formatDate 硬编码 "en-US"。
方案
export function formatDate(date: string | Date, locale = "zh-CN") {
return new Intl.DateTimeFormat(locale, {
year: "numeric",
month: "short",
day: "numeric",
}).format(new Date(date))
}
P3-5 AI 聊天接口速率限制
问题: /api/ai/chat 需确认是否有服务端速率限制。
方案
- 使用
@upstash/ratelimit或自实现基于 IP/用户的速率限制 - 建议限制:每用户每分钟 10 次请求
执行时间线
| 阶段 | 内容 | 预计周期 |
|---|---|---|
| 第 1 周 | P0-1 exam-form.tsx 拆分 | 3 天 |
| 第 1 周 | P0-1 textbook-reader.tsx 拆分 | 2 天 |
| 第 2 周 | P0-2 无障碍修复 | 3 天 |
| 第 2 周 | P1-1 创建 Hooks 层 | 2 天 |
| 第 3 周 | P1-2 动画降级 | 1 天 |
| 第 3 周 | P1-3 图片/字体优化 | 2 天 |
| 第 3-4 周 | P2-1 补充单元测试 | 5 天 |
| 第 4 周 | P2-2 + P2-3 配置清理 | 2 天 |
| 持续 | P3 优化项 | 随迭代推进 |
验收检查清单
- 单文件不超过 300 行
- axe-core 扫描零严重 a11y 违规
- Lighthouse Accessibility ≥ 90
- Lighthouse Performance ≥ 85
- 关键路径测试覆盖率 > 80%
npm run build零错误零警告npm run lint零错误npm run typecheck零错误- 无
<div onClick>/<span onClick>模式 - 所有图标按钮有
aria-label prefers-reduced-motion降级生效- 生产构建不含 mock 数据