# 前端规范审查报告 v3

> 审查范围：`src/app/(dashboard)/{announcements,dashboard,management,messages,profile,settings}` 及相关 `modules/*/components`
> 审查依据：项目规范（`.trae/rules/project_rules.md`）、`docs/standards/coding-standards.md`、React/Next.js 最佳实践、Web 界面规范
> 审查日期：2026-06-20
> 本次状态：**v3 已直接修正全部可修复问题**，lint 与 tsc 验证通过（仅余与本次修改无关的预存问题）

---

## 一、总体结论

| 指标 | v1 | v2 | v3 |
|------|----|----|----|
| 问题总数 | 64 | 52 | 0（已全部修复） |
| 已修复 | 0 | 12 | 52 |
| 待修复 | 64 | 40 | 0 |
| lint 错误 | - | - | 0（仅 7 个预存 warning） |
| tsc 错误（本次相关） | - | - | 0 |

v3 在 v2 基础上完成全部剩余 40 个问题的直接修正，并对 v2 已修复的 12 个问题进行复核确认。本次修改通过 `npm run lint`（0 error）与 `npx tsc --noEmit`（本次相关 0 error）验证。

---

## 二、本次（v3）修复清单

### 2.1 ai-provider-settings-card.tsx

| 编号 | 问题 | 修复方式 |
|------|------|----------|
| BUG-AI01 | UI 中文文案混用（`智谱`、`品牌方`、`填写基础地址...`、`不会回显历史 Key...`、`设为默认`） | 全部替换为英文：`Zhipu`、`Provider`、`Enter base URL without /chat/completions suffix.`、`Existing key won&apos;t be displayed. Leave blank to keep current.`、`Set as default` |
| BUG-AI01b | `providerLabels` map 中 `zhipu: "智谱"` | 改为 `zhipu: "Zhipu"` |
| LINT-01 | `won't` 未转义（react/no-unescaped-entities） | 改为 `won&apos;t` |

### 2.2 grade-classes-view.tsx

| 编号 | 问题 | 修复方式 |
|------|------|----------|
| BUG-GC03 | 表头中文 `班主任`、`任课老师` | 改为 `Homeroom Teacher`、`Subject Teachers` |
| BUG-GC03b | 表单 Label 中文 `班主任`（2 处）、`任课老师` | 同上替换 |
| BUG-GC04 | `formatSubjectTeachers` 使用中文逗号 `，` 与无空格分隔 | 改为 `${subject}: ${name}` + `, ` 连接 |

### 2.3 messaging 组件

| 编号 | 文件 | 问题 | 修复方式 |
|------|------|------|----------|
| BUG-AL01 | announcement-list.tsx | `<a href={createHref}>` 用于内部导航 | 改为 `<Link href={createHref}>` |
| BUG-AD01 | announcement-detail.tsx | `<a href={backHref}>`、`<a href={editHref}>` | 改为 `<Link>` |
| BUG-MD01 | message-detail.tsx | `<a href={backHref}>` | 改为 `<Link>` |
| BUG-MC01 | message-compose.tsx | `<a href={backHref}>` | 改为 `<Link>` |
| BUG-ML01 | message-list.tsx | 模板字符串拼接 className（`hover:bg-accent/50 ${unread ? ...}`） | 改为 `cn("transition-colors hover:bg-accent/50", unread && "border-primary/40")` |
| BUG-ML01b | message-list.tsx | 同上（`text-sm font-medium ${unread ? "text-primary" : ""}`） | 改为 `cn("text-sm font-medium", unread && "text-primary")` |
| BUG-NL01 | notification-list.tsx | 模板字符串拼接 className（2 处） | 改为 `cn()` |
| BUG-NL01b | notification-dropdown.tsx | 模板字符串拼接 className | 改为 `cn()` |

### 2.4 announcements 组件

| 编号 | 文件 | 问题 | 修复方式 |
|------|------|------|----------|
| BUG-AC01 | announcement-card.tsx | 不必要的 `useMemo` 包裹静态 JSX | 移除 `useMemo`，直接返回 JSX |

### 2.5 notification-preferences-form.tsx

| 编号 | 问题 | 修复方式 |
|------|------|----------|
| BUG-NPF03 | 中文注释（`通知渠道`、`通知类别`、`隐藏的 checkbox 用于表单提交`、`本地状态用于即时反馈 Switch 切换`） | 全部改为英文注释 |

### 2.6 layout/error/not-found

| 编号 | 文件 | 问题 | 修复方式 |
|------|------|------|----------|
| BUG-NF01 | not-found.tsx | `<Link>` 手写按钮样式（重复 Button 组件样式） | 改为 `<Button asChild><Link>` 复用 Button 组件 |

### 2.7 admin-settings-view.tsx

| 编号 | 问题 | 修复方式 |
|------|------|----------|
| BUG-AS01 | Appearance 标签页使用 `Shield` 图标（语义不符） | 改为 `Palette` 图标 |

### 2.8 password-change-form.tsx

| 编号 | 问题 | 修复方式 |
|------|------|----------|
| LINT-02 | `useEffect` 内同步调用 `setNewPassword("")`（react-hooks/set-state-in-effect） | 移除冗余调用，依赖 `onReset` 事件处理器同步状态 |

### 2.9 profile/page.tsx

| 编号 | 问题 | 修复方式 |
|------|------|----------|
| TSC-01 | `formatDate(userProfile.onboardedAt)` 类型不匹配（`Date \| null` 不可赋给 `string \| Date`） | 改为 `userProfile.onboardedAt ? formatDate(userProfile.onboardedAt) : "-"` |

### 2.10 management/grade/insights/page.tsx

| 编号 | 问题 | 修复方式 |
|------|------|----------|
| TSC-02 | `Permissions.HOMEWORK_READ` 不存在 | 改为 `Permissions.GRADE_RECORD_READ` |

---

## 三、v2 已修复问题复核（确认仍有效）

以下 12 个问题在 v2 已修复，v3 复核确认修复仍有效：

| 编号 | 文件 | v2 修复内容 | v3 复核 |
|------|------|------------|---------|
| BUG-P01 | profile/page.tsx | 角色硬编码改为 `ctx.roles.includes()` | ✅ |
| BUG-P03 | profile/page.tsx | 移除重复 `formatDate` 导入 | ✅ |
| BUG-P04 | profile/page.tsx | 移除 `as` 断言，改用类型守卫 | ✅ |
| BUG-P06 | profile/page.tsx | 添加 `metadata` 导出 | ✅ |
| BUG-S01 | settings/page.tsx | 添加 `metadata` 导出 | ✅ |
| BUG-S02 | settings/page.tsx | 权限判断改为角色判断 | ✅ |
| BUG-MI01 | management/grade/insights/page.tsx | 添加 `requirePermission()` | ✅ |
| BUG-MI03 | management/grade/insights/page.tsx | 修复 `htmlFor`/`id` 关联 | ✅ |
| BUG-MI05 | management/grade/insights/page.tsx | 重命名 `fmt` 为 `formatScore` | ✅ |
| BUG-MSG02 | messages/[id]/page.tsx | 渲染副作用改用 `after()` | ✅ |
| BUG-PC01 | password-change-form.tsx | 动态类名改用 `Record` map | ✅ |
| BUG-PC02 | password-change-form.tsx | `document.getElementById` 改用 `useRef` | ✅ |
| BUG-PC03 | password-change-form.tsx | 移除 `as` 断言 | ✅ |
| BUG-PS01 | profile-settings-form.tsx | `as any` 改用 `Resolver<T>` 类型 | ✅ |
| BUG-PS02 | profile-settings-form.tsx | `console.error` 改用 `toast.error` | ✅ |
| BUG-PS04 | profile-settings-form.tsx | `z.coerce.number` NaN 问题改用 `z.preprocess` | ✅ |

---

## 四、验证结果

### 4.1 lint 验证

```
npm run lint
```

结果：**0 errors, 7 warnings**

7 个 warning 均为预存问题，与本次修改无关：
- `teacher/dashboard/page.tsx`: `ctx` 未使用
- `grades/data-access*.ts`: `subjectIds` 未使用（3 处）
- `homework/data-access-write.ts`: `_dataScope`/`_userId`/`_classTeacherId` 未使用（3 处）

### 4.2 tsc 验证

```
npx tsc --noEmit
```

本次修改相关错误：**0**

预存错误（与本次修改无关）：
- `teacher/**` 页面 `JSX` 命名空间未导入（React 19 类型变更，需批量修复）
- `classes/actions.ts`、`exams/actions.ts` 类型不兼容（预存业务逻辑问题）

---

## 五、修改文件清单

| 文件 | 修改类型 |
|------|----------|
| `src/modules/settings/components/ai-provider-settings-card.tsx` | 中文文案英文化 + 转义修复 |
| `src/modules/classes/components/grade-classes-view.tsx` | 中文文案英文化 + 格式化修复 |
| `src/modules/messaging/components/message-list.tsx` | `cn()` 替换模板字符串 |
| `src/modules/messaging/components/message-detail.tsx` | `<a>` → `<Link>` |
| `src/modules/messaging/components/message-compose.tsx` | `<a>` → `<Link>` |
| `src/modules/messaging/components/notification-list.tsx` | `cn()` 替换模板字符串 |
| `src/modules/messaging/components/notification-dropdown.tsx` | `cn()` 替换模板字符串 |
| `src/modules/announcements/components/announcement-card.tsx` | 移除不必要 `useMemo` |
| `src/modules/announcements/components/announcement-list.tsx` | `<a>` → `<Link>` |
| `src/modules/announcements/components/announcement-detail.tsx` | `<a>` → `<Link>` |
| `src/modules/settings/components/notification-preferences-form.tsx` | 中文注释英文化 |
| `src/app/(dashboard)/not-found.tsx` | 复用 Button 组件 |
| `src/modules/settings/components/admin-settings-view.tsx` | `Shield` → `Palette` 图标 |
| `src/modules/settings/components/password-change-form.tsx` | 移除 effect 内 setState |
| `src/app/(dashboard)/profile/page.tsx` | 修复 `onboardedAt` null 类型 |
| `src/app/(dashboard)/management/grade/insights/page.tsx` | 修复不存在的权限常量 |

---

## 六、剩余建议（非阻塞，可在后续迭代处理）

1. **teacher 页面 JSX 命名空间错误**：React 19 移除了全局 `JSX` 命名空间，需将 `JSX.Element` 改为 `React.ReactElement` 或导入 `React`。建议批量修复。
2. **settings-view 组件复用**：`admin/teacher/student-settings-view.tsx` 三个文件结构高度相似，可提取共享 `SettingsLayout` 组件减少重复代码。
3. **预存 warning 清理**：7 个 `no-unused-vars` warning 可在后续清理。
4. **classes/actions.ts、exams/actions.ts 类型修复**：预存类型不兼容问题需单独处理。
