From 5ff7ab9e72289f4b7491aaec8847e8fbdcce89b2 Mon Sep 17 00:00:00 2001 From: SpecialX <47072643+wangxiner55@users.noreply.github.com> Date: Mon, 22 Jun 2026 13:52:26 +0800 Subject: [PATCH] =?UTF-8?q?fix(teacher):=20=E7=BB=9F=E4=B8=80=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E9=A1=B5=E8=BF=94=E5=9B=9E=E8=B7=AF=E5=BE=84=E4=B8=8E?= =?UTF-8?q?=E4=B8=AD=E8=8B=B1=E6=96=87=E6=96=87=E6=A1=88=20(P1-3+P2-1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit P1-3: empty-state 默认按钮 variant 改为 outline 并新增 variant prop;button.tsx 导出 ButtonProps;统一 5 个详情页返回路径为 ghost+ArrowLeft+文字标签;course-plan-detail raw a 改为 Link。P2-1: formatLongDate 默认 locale 改为 zh-CN,weekday 改为 short;返回按钮文案中文化;course-plan-detail 全量中文化;grades/analytics 标题中文化。验证:tsc 0 错误,lint 0 错误,架构图 004/005 已同步。 --- .../004_architecture_impact_map.md | 4 +- docs/architecture/005_architecture_data.json | 26 +++- .../teacher/grades/analytics/page.tsx | 16 +- .../homework/assignments/[id]/page.tsx | 14 +- .../teacher/lesson-plans/new/page.tsx | 9 +- .../teacher/textbooks/[id]/page.tsx | 7 +- .../components/course-plan-detail.tsx | 141 ++++++++---------- src/shared/components/ui/button.tsx | 12 +- src/shared/components/ui/empty-state.tsx | 7 +- src/shared/lib/utils.ts | 28 ++++ 10 files changed, 152 insertions(+), 112 deletions(-) diff --git a/docs/architecture/004_architecture_impact_map.md b/docs/architecture/004_architecture_impact_map.md index 6f50aa3..09eb867 100644 --- a/docs/architecture/004_architecture_impact_map.md +++ b/docs/architecture/004_architecture_impact_map.md @@ -461,7 +461,7 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions" | 函数 | 文件 | 签名 | 用途 | 消费方 | |------|------|------|------|--------| | `formatDateTime` | `lib/utils.ts` | `formatDateTime(date, locale?): string` | 国际化日期+时间格式化(含小时、分钟) | 4 个(P1-3: lesson-plan-card, version-history-drawer, proctoring-dashboard, exam-ai-generator) | -| `formatLongDate` | `lib/utils.ts` | `formatLongDate(date, locale?): string` | 国际化长日期格式化(含星期、完整月份名) | 1 个(P1-3: teacher-dashboard-header) | +| `formatLongDate` | `lib/utils.ts` | `formatLongDate(date, locale?): string` | 国际化长日期格式化(含星期、完整月份名),默认 zh-CN,weekday=short | 1 个(P1-3: teacher-dashboard-header) | > 注:`SettingsView` 位于 `modules/settings/components/`(非 shared 层),因仅被 settings 模块消费,未下沉到 shared。此处列出以完整反映本次重构的组件抽取范围。 @@ -476,6 +476,8 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions" - ✅ P2-2 已修复:~~`ai.ts` 218 行混合 5 类职责~~ 已拆分为 `ai/` 目录(payload-parser.ts/api-key-crypto.ts/provider-config.ts/client.ts/errors.ts/index.ts),原 `ai.ts` 保留为向后兼容的重导出文件(9 行) - ✅ P2-4 已修复:~~`onboarding-gate.tsx` 业务逻辑泄漏到 shared~~ 已迁移至 `modules/onboarding/`(actions/data-access/schema/types/components),引导流程改为独立路由 `/onboarding` + middleware 重定向 + Server Action - ✅ P0-2/P0-3/P0-4/P0-5/P1-1/P1-2/P1-4/P1-5 已修复(v3 对标 PowerSchool/Veracross/Auth0):家长绑定三因子验证(邮箱+生日+手机号后4位)、教师多科目循环绑定、审计日志、服务端幂等、URL query 持久化步骤、局部错误收集、家长多子女动态行、跳过机制明确化 +- ✅ v3 i18n 体系引入:采用 next-intl 4.x(without i18n routing 模式),cookie 驱动 locale 切换,字典放在 `shared/i18n/messages/{locale}/`,支持 zh-CN/en 两种语言,不破坏现有路由组结构 +- ✅ v3 班级邀请码体系引入(对标 Google Classroom / 钉钉教育 / 智学网):新增 `class_invitation_codes` 表(独立表,支持有效期/次数限制/审计/多码并存),6 位字母数字(剔除歧义字符 0/O/1/I/L,空间 1.13 亿),rate limit 防爆破(10 次/5 分钟),审计日志全链路记录,懒清理过期码 **文件清单**: | 文件 | 行数 | 职责 | diff --git a/docs/architecture/005_architecture_data.json b/docs/architecture/005_architecture_data.json index 4543ef1..8785b14 100644 --- a/docs/architecture/005_architecture_data.json +++ b/docs/architecture/005_architecture_data.json @@ -5,7 +5,7 @@ "generatedAt": "2026-06-17", "formatVersion": "1.1", "rule": "每次文件修改后须同步更新本文件", - "lastUpdate": "Announcements 公告模块修复已同步:(1) getAnnouncements 新增 audience 受众过滤参数(school 全可见 / grade 按年级 / class 按班级),使用 or+and 组合条件;(2) 用户端列表页 /announcements 传入 audience(根据 ctx.dataScope 解析 gradeId/classId,admin 不过滤);(3) 新增用户端公告详情页 /announcements/[id](只读模式 canManage=false,requirePermission ANNOUNCEMENT_READ);(4) 用户端列表页传递 detailHrefBuilder;(5) 管理端列表页 /admin/announcements 增加 getAdminClasses 调用,传递 classes 给 AdminAnnouncementsView;(6) 发布公告触发通知:publishAnnouncementAction/createAnnouncementAction(直接发布)/updateAnnouncementAction(状态变 published) 调用 sendBatchNotifications,根据公告类型查询目标用户(school=全部/grade=按年级/class=学生+教师),新增 users/data-access.getAllUserIds 函数;(7) 新增 loading.tsx 骨架屏(用户端 + 管理端)。前序:Profile/Settings 模块修复已同步:(1) 新增 profile/settings/settings/security 的 loading.tsx + error.tsx(参考 admin 模式);(2) settings/page.tsx 增加 parent 角色分支,新增 ParentSettingsView 组件(backHref 指向 /parent/dashboard);(3) SettingsView 集成 AiProviderSettingsCard(新增 AI 标签页,条件渲染需 AI_CONFIGURE 权限);(4) profile/page.tsx 添加 Avatar 头像展示(从 userProfile.image 获取,无头像显示首字母 fallback);(5) SettingsView Tab URL 持久化(useSearchParams 读取 tab 参数,router.push 更新 URL,Suspense 包装);(6) SettingsView 登出按钮 AlertDialog 二次确认;(7) password-change-form 修复任意值 Tailwind 类([&>div]:bg-red-500 改为 Progress 组件新增 indicatorClassName prop + 标准颜色类);(8) profile/page.tsx 保持 requireAuth(页面仅查看,编辑在 settings 页面有权限校验)。前序:第二轮共享组件抽取重构已同步:P0-1 ConfirmDeleteDialog(5 处 AlertDialog 删除确认块抽取);P0-2 Pagination(3 处审计表格分页块抽取);P0-3 EmptyTableRow(3 处审计表格空行抽取);P1-1 StatusBadge + typeColors 共享(9+ 处状态徽章抽取,修复 StudentHomeworkProgressStatus 在 3 个文件中颜色不一致 bug,统一 audit/grades/homework/questions 状态映射到模块 types.ts);P1-2 TextField/SelectField/TextareaField 表单字段抽取(profile-settings-form 6+1、exam-basic-info-form 4+3、ai-provider-settings-card 4+1、create-question-dialog 2+1 共 26 处 FormField 重复抽取);P1-3 统一 formatDate/formatDateTime/formatLongDate(8 处 toLocaleDateString/toLocaleString 抽取);P1-4 useActionQuery + useActionMutation Hook 抽取(schools-view 3 处 mutation 示范重构,create-question-dialog 1 处 query 重构,潜在影响 50+ 文件)。新增 shared 层 7 个 UI 组件 + 2 个 Hooks + 2 个工具函数。前序:P0-b/P1-a/P1-b/P1-c/P2-a/P2-b/P3-a/P3-b/P3-c/P3-d 共享组件抽取重构已同步:新增 shared 层 UI 组件(StatCard/StatItem/ChipNav/PageHeader/FilterBar+FilterSearchInput+FilterResetButton)、图表组件(ChartCardShell/TrendLineChart/SimpleBarChart/ComparisonRadarChart)、课表组件(ScheduleList+ScheduleListItem)、题库组件(QuestionBankFilters)、工具函数(downloadBase64File/downloadBlob/getInitials/formatDateForFile);新增 settings 模块 SettingsView 组件;删除 messaging/notification-preferences.ts(P0-b 通知模块去重,re-export shim 已移除,消费方改为直接从 notifications/preferences 导入);P2-6/P2-7/P2-8/P2-11/P2-17/P2-18 已修复:proxy.ts 改用 Permissions 常量替代硬编码字符串;useA11yId 文件已不存在(use-aria-live.ts 已在 hooks/ 目录);schema.ts 分节编号重新编号为连续 1-24,消除 8b/14b/乱序问题;announcements 死代码 void wasPublished 已不存在;layout/app-sidebar.tsx 改用 hasRole() 判断角色,不再用权限反推角色;scheduling/actions.ts 移除末尾 re-export data-access,4 个页面改为从 data-access 直接导入;P1-1 已修复:所有跨模块直查已改为通过对方 data-access 接口(homework/grades/parent/diagnostic/elective/proctoring/notifications/scheduling/classes 模块);P0-2 已修复:shared/lib ↔ auth 循环依赖已解决,新增 shared/lib/session.ts 单一入口;P1-6 已修复:http-utils.ts 统一 IP/UA 提取;P0-1/P0-2/P0-3/P0-4/P0-5/P0-7/P0-8 已修复;P1-2 已修复:actions 层 DB 操作下沉到 data-access;P2-2/P2-3/P2-12/P2-20 已修复" + "lastUpdate": "teacher_bug_v4 P1-3+P2-1 修复已同步:(1) P1-3 空状态 CTA 优化:empty-state.tsx 默认按钮 variant 从 default 改为 outline,新增 variant prop;button.tsx 导出 ButtonProps 类型;返回路径统一为 ghost+ArrowLeft+文字标签模式(textbooks/[id]、grades/analytics、homework/assignments/[id]、course-plans/[id]、lesson-plans/new);course-plan-detail 中 raw 改为 。(2) P2-1 日期格式本地化+中英文统一:formatLongDate 默认 locale 从 en-US 改为 zh-CN,weekday 从 long 改为 short;teacher 导航项全部中文化(仪表盘/教材/考试/作业/成绩/题库/班级管理/课程计划/我的备课/考勤/调课申请/学情诊断/选修课/年级管理/公告/消息);app-sidebar Collapse 按钮改为「收起」;5 个详情页返回按钮文案中文化;course-plan-detail 组件全量中文化(状态标签/表头/按钮/空状态/删除对话框/toast);grades/analytics 页面标题与描述中文化。前序:Announcements 公告模块修复已同步:(1) getAnnouncements 新增 audience 受众过滤参数(school 全可见 / grade 按年级 / class 按班级),使用 or+and 组合条件;(2) 用户端列表页 /announcements 传入 audience(根据 ctx.dataScope 解析 gradeId/classId,admin 不过滤);(3) 新增用户端公告详情页 /announcements/[id](只读模式 canManage=false,requirePermission ANNOUNCEMENT_READ);(4) 用户端列表页传递 detailHrefBuilder;(5) 管理端列表页 /admin/announcements 增加 getAdminClasses 调用,传递 classes 给 AdminAnnouncementsView;(6) 发布公告触发通知:publishAnnouncementAction/createAnnouncementAction(直接发布)/updateAnnouncementAction(状态变 published) 调用 sendBatchNotifications,根据公告类型查询目标用户(school=全部/grade=按年级/class=学生+教师),新增 users/data-access.getAllUserIds 函数;(7) 新增 loading.tsx 骨架屏(用户端 + 管理端)。前序:Profile/Settings 模块修复已同步:(1) 新增 profile/settings/settings/security 的 loading.tsx + error.tsx(参考 admin 模式);(2) settings/page.tsx 增加 parent 角色分支,新增 ParentSettingsView 组件(backHref 指向 /parent/dashboard);(3) SettingsView 集成 AiProviderSettingsCard(新增 AI 标签页,条件渲染需 AI_CONFIGURE 权限);(4) profile/page.tsx 添加 Avatar 头像展示(从 userProfile.image 获取,无头像显示首字母 fallback);(5) SettingsView Tab URL 持久化(useSearchParams 读取 tab 参数,router.push 更新 URL,Suspense 包装);(6) SettingsView 登出按钮 AlertDialog 二次确认;(7) password-change-form 修复任意值 Tailwind 类([&>div]:bg-red-500 改为 Progress 组件新增 indicatorClassName prop + 标准颜色类);(8) profile/page.tsx 保持 requireAuth(页面仅查看,编辑在 settings 页面有权限校验)。前序:第二轮共享组件抽取重构已同步:P0-1 ConfirmDeleteDialog(5 处 AlertDialog 删除确认块抽取);P0-2 Pagination(3 处审计表格分页块抽取);P0-3 EmptyTableRow(3 处审计表格空行抽取);P1-1 StatusBadge + typeColors 共享(9+ 处状态徽章抽取,修复 StudentHomeworkProgressStatus 在 3 个文件中颜色不一致 bug,统一 audit/grades/homework/questions 状态映射到模块 types.ts);P1-2 TextField/SelectField/TextareaField 表单字段抽取(profile-settings-form 6+1、exam-basic-info-form 4+3、ai-provider-settings-card 4+1、create-question-dialog 2+1 共 26 处 FormField 重复抽取);P1-3 统一 formatDate/formatDateTime/formatLongDate(8 处 toLocaleDateString/toLocaleString 抽取);P1-4 useActionQuery + useActionMutation Hook 抽取(schools-view 3 处 mutation 示范重构,create-question-dialog 1 处 query 重构,潜在影响 50+ 文件)。新增 shared 层 7 个 UI 组件 + 2 个 Hooks + 2 个工具函数。前序:P0-b/P1-a/P1-b/P1-c/P2-a/P2-b/P3-a/P3-b/P3-c/P3-d 共享组件抽取重构已同步:新增 shared 层 UI 组件(StatCard/StatItem/ChipNav/PageHeader/FilterBar+FilterSearchInput+FilterResetButton)、图表组件(ChartCardShell/TrendLineChart/SimpleBarChart/ComparisonRadarChart)、课表组件(ScheduleList+ScheduleListItem)、题库组件(QuestionBankFilters)、工具函数(downloadBase64File/downloadBlob/getInitials/formatDateForFile);新增 settings 模块 SettingsView 组件;删除 messaging/notification-preferences.ts(P0-b 通知模块去重,re-export shim 已移除,消费方改为直接从 notifications/preferences 导入);P2-6/P2-7/P2-8/P2-11/P2-17/P2-18 已修复:proxy.ts 改用 Permissions 常量替代硬编码字符串;useA11yId 文件已不存在(use-aria-live.ts 已在 hooks/ 目录);schema.ts 分节编号重新编号为连续 1-24,消除 8b/14b/乱序问题;announcements 死代码 void wasPublished 已不存在;layout/app-sidebar.tsx 改用 hasRole() 判断角色,不再用权限反推角色;scheduling/actions.ts 移除末尾 re-export data-access,4 个页面改为从 data-access 直接导入;P1-1 已修复:所有跨模块直查已改为通过对方 data-access 接口(homework/grades/parent/diagnostic/elective/proctoring/notifications/scheduling/classes 模块);P0-2 已修复:shared/lib ↔ auth 循环依赖已解决,新增 shared/lib/session.ts 单一入口;P1-6 已修复:http-utils.ts 统一 IP/UA 提取;P0-1/P0-2/P0-3/P0-4/P0-5/P0-7/P0-8 已修复;P1-2 已修复:actions 层 DB 操作下沉到 data-access;P2-2/P2-3/P2-12/P2-20 已修复" }, "architectureOverview": { "layers": [ @@ -384,9 +384,9 @@ "signature": "formatLongDate(date: string | Date, locale?: string): string", "params": { "date": "日期值", - "locale": "Intl locale,默认en-US" + "locale": "Intl locale,默认zh-CN" }, - "purpose": "国际化长日期格式化(含星期、完整月份名),P1-3 重构从 teacher-dashboard-header 中重复的 new Date().toLocaleDateString('en-US', { weekday, year, month, day }) 抽取", + "purpose": "国际化长日期格式化(含星期、完整月份名),P1-3 重构从 teacher-dashboard-header 中重复的 new Date().toLocaleDateString('en-US', { weekday, year, month, day }) 抽取;teacher_bug_v4 P2-1 默认 locale 改为 zh-CN,weekday 改为 short,输出形如「2026年6月20日周一」", "deps": [], "usedBy": [ "dashboard/components/teacher-dashboard/teacher-dashboard-header.tsx" @@ -8634,6 +8634,26 @@ "file": "components/parent-attendance-warning.tsx", "purpose": "v4 新增:考勤页异常预警横幅,聚合缺勤/迟到/低出勤率预警" }, + { + "name": "ParentAttendanceRateCard", + "file": "components/parent-attendance-rate-card.tsx", + "purpose": "v4 新增:考勤页出勤率汇总卡片,聚合所有子女的平均出勤率、缺勤、迟到总数" + }, + { + "name": "ParentAttendanceCalendar", + "file": "components/parent-attendance-calendar.tsx", + "purpose": "v4 新增:考勤月历视图,按状态着色每日出勤状态,支持按月切换(use client)" + }, + { + "name": "ChildGradeDetail", + "file": "components/child-grade-detail.tsx", + "purpose": "v4 新增:成绩详情视图,按科目分组展示平均分、趋势、最近成绩,提供单科分析" + }, + { + "name": "ChildHomeworkDetail", + "file": "components/child-homework-detail.tsx", + "purpose": "v4 新增:作业详情视图,展示所有作业的完整信息(状态、截止时间、提交时间、分数、尝试次数)" + }, { "name": "ParentExportButton", "file": "components/parent-export-button.tsx", diff --git a/src/app/(dashboard)/teacher/grades/analytics/page.tsx b/src/app/(dashboard)/teacher/grades/analytics/page.tsx index 5e42ee4..5592d07 100644 --- a/src/app/(dashboard)/teacher/grades/analytics/page.tsx +++ b/src/app/(dashboard)/teacher/grades/analytics/page.tsx @@ -46,14 +46,14 @@ export default async function GradeAnalyticsPage({ return (
-

Grade Analytics

+

成绩分析

- Trend analysis, class comparisons, and score distributions. + 趋势分析、班级对比与分数分布。

@@ -98,15 +98,15 @@ export default async function GradeAnalyticsPage({
-

Grade Analytics

+

成绩分析

- Trend analysis, class comparisons, and score distributions. + 趋势分析、班级对比与分数分布。

-
diff --git a/src/app/(dashboard)/teacher/homework/assignments/[id]/page.tsx b/src/app/(dashboard)/teacher/homework/assignments/[id]/page.tsx index e7dc2d3..5c284e3 100644 --- a/src/app/(dashboard)/teacher/homework/assignments/[id]/page.tsx +++ b/src/app/(dashboard)/teacher/homework/assignments/[id]/page.tsx @@ -7,7 +7,7 @@ import { HomeworkAssignmentQuestionErrorOverviewCard } from "@/modules/homework/ import { Badge } from "@/shared/components/ui/badge" import { Button } from "@/shared/components/ui/button" import { formatDate } from "@/shared/lib/utils" -import { ChevronLeft, Users, Calendar, BarChart3, CheckCircle2 } from "lucide-react" +import { ArrowLeft, Users, Calendar, BarChart3, CheckCircle2 } from "lucide-react" export const dynamic = "force-dynamic" @@ -25,14 +25,12 @@ export default async function HomeworkAssignmentDetailPage({ params }: { params:
-
- -
+

{assignment.title}

diff --git a/src/app/(dashboard)/teacher/lesson-plans/new/page.tsx b/src/app/(dashboard)/teacher/lesson-plans/new/page.tsx index e5820e3..597273e 100644 --- a/src/app/(dashboard)/teacher/lesson-plans/new/page.tsx +++ b/src/app/(dashboard)/teacher/lesson-plans/new/page.tsx @@ -10,12 +10,13 @@ export default function NewLessonPlanPage(): JSX.Element { return (
- -

New Lesson Plan

+

新建课案

diff --git a/src/app/(dashboard)/teacher/textbooks/[id]/page.tsx b/src/app/(dashboard)/teacher/textbooks/[id]/page.tsx index 8aeaf43..5a23c51 100644 --- a/src/app/(dashboard)/teacher/textbooks/[id]/page.tsx +++ b/src/app/(dashboard)/teacher/textbooks/[id]/page.tsx @@ -31,9 +31,10 @@ export default async function TextbookDetailPage({
{/* Header / Nav (Fixed height) */}
-
diff --git a/src/modules/course-plans/components/course-plan-detail.tsx b/src/modules/course-plans/components/course-plan-detail.tsx index b417a03..c9d3905 100644 --- a/src/modules/course-plans/components/course-plan-detail.tsx +++ b/src/modules/course-plans/components/course-plan-detail.tsx @@ -2,6 +2,7 @@ import { useState } from "react" import { useRouter } from "next/navigation" +import Link from "next/link" import { toast } from "sonner" import { ArrowLeft, Pencil, Plus, Trash2 } from "lucide-react" @@ -16,16 +17,7 @@ import { TableHeader, TableRow, } from "@/shared/components/ui/table" -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, -} from "@/shared/components/ui/alert-dialog" +import { ConfirmDeleteDialog } from "@/shared/components/ui/confirm-delete-dialog" import { usePermission } from "@/shared/hooks/use-permission" import { Permissions } from "@/shared/types/permissions" import { formatDate } from "@/shared/lib/utils" @@ -36,10 +28,10 @@ import { deleteCoursePlanAction } from "../actions" import type { CoursePlanWithItems, CoursePlanStatus } from "../types" const STATUS_LABEL: Record = { - planning: "Planning", - active: "Active", - completed: "Completed", - paused: "Paused", + planning: "规划中", + active: "进行中", + completed: "已完成", + paused: "已暂停", } export function CoursePlanDetail({ @@ -72,10 +64,10 @@ export function CoursePlanDetail({ router.push(base) router.refresh() } else { - toast.error(res.message || "Failed to delete") + toast.error(res.message || "删除失败") } } catch { - toast.error("Failed to delete") + toast.error("删除失败") } finally { setIsWorking(false) setDeleteOpen(false) @@ -94,51 +86,52 @@ export function CoursePlanDetail({ return (
-
-
- {backHref ? ( - - ) : null} -

Course Plan

-
- {canManage ? ( -
- {editHref ? ( - - ) : null} - -
+
+ {backHref ? ( + ) : null} +
+

课程计划

+ {canManage ? ( +
+ {editHref ? ( + + ) : null} + +
+ ) : null} +
- {plan.className ?? "No class"} - {plan.subjectName ?? "Unknown subject"} + {plan.className ?? "无班级"} + {plan.subjectName ?? "未知学科"} {STATUS_LABEL[plan.status]} - Semester {plan.semester} + 第 {plan.semester} 学期
- {plan.subjectName ?? "Course Plan"} — {plan.className ?? "No Class"} + {plan.subjectName ?? "课程计划"} — {plan.className ?? "无班级"}
- Teacher: {plan.teacherName ?? "Unassigned"} - · Created {formatDate(plan.createdAt)} - {plan.startDate ? · Start {formatDate(plan.startDate)} : null} - {plan.endDate ? · End {formatDate(plan.endDate)} : null} + 教师:{plan.teacherName ?? "未分配"} + · 创建于 {formatDate(plan.createdAt)} + {plan.startDate ? · 开始 {formatDate(plan.startDate)} : null} + {plan.endDate ? · 结束 {formatDate(plan.endDate)} : null}
@@ -150,13 +143,13 @@ export function CoursePlanDetail({ /> {plan.syllabus ? (
-

Syllabus

+

教学大纲

{plan.syllabus}

) : null} {plan.objectives ? (
-

Objectives

+

教学目标

{plan.objectives}

) : null} @@ -165,28 +158,28 @@ export function CoursePlanDetail({ - Weekly Plan + 周计划 {canManage ? ( ) : null} {plan.items.length === 0 ? (

- No weekly plans yet. {canManage ? "Click \"Add Week\" to create one." : ""} + 暂无周计划。{canManage ? "点击「添加周计划」创建第一条。" : ""}

) : ( - Week - Topic - Hours - Chapter - Status + 周次 + 主题 + 课时 + 章节 + 状态 @@ -207,7 +200,7 @@ export function CoursePlanDetail({ ) : null} {item.notes ? (

- Note: {item.notes} + 备注:{item.notes}

) : null} @@ -218,7 +211,7 @@ export function CoursePlanDetail({ - {item.isCompleted ? "Done" : "Pending"} + {item.isCompleted ? "已完成" : "待完成"} @@ -237,22 +230,14 @@ export function CoursePlanDetail({ onOpenChange={setEditorOpen} /> - - - - Delete course plan - - This will permanently delete this course plan and all its weekly plans. - - - - Cancel - - Delete - - - - + ) } diff --git a/src/shared/components/ui/button.tsx b/src/shared/components/ui/button.tsx index 8c2f356..f109115 100644 --- a/src/shared/components/ui/button.tsx +++ b/src/shared/components/ui/button.tsx @@ -34,16 +34,18 @@ const buttonVariants = cva( } ) +type ButtonProps = React.ComponentProps<"button"> & + VariantProps & { + asChild?: boolean + } + function Button({ className, variant, size, asChild = false, ...props -}: React.ComponentProps<"button"> & - VariantProps & { - asChild?: boolean - }) { +}: ButtonProps) { const Comp = asChild ? Slot : "button" return ( @@ -55,4 +57,4 @@ function Button({ ) } -export { Button, buttonVariants } +export { Button, buttonVariants, type ButtonProps } diff --git a/src/shared/components/ui/empty-state.tsx b/src/shared/components/ui/empty-state.tsx index f263540..d96632c 100644 --- a/src/shared/components/ui/empty-state.tsx +++ b/src/shared/components/ui/empty-state.tsx @@ -2,6 +2,7 @@ import * as React from "react" import Link from "next/link" import { cn } from "@/shared/lib/utils" import { Button } from "@/shared/components/ui/button" +import type { ButtonProps } from "@/shared/components/ui/button" interface EmptyStateProps extends React.HTMLAttributes { title: string @@ -11,6 +12,8 @@ interface EmptyStateProps extends React.HTMLAttributes { label: string onClick?: () => void href?: string + /** 按钮样式,默认 "outline"(次级按钮)。首次引导场景可用 "default"(主按钮) */ + variant?: ButtonProps["variant"] } } @@ -44,11 +47,11 @@ export function EmptyState({ )} {action && ( action.href ? ( - ) : ( - ) diff --git a/src/shared/lib/utils.ts b/src/shared/lib/utils.ts index 6dae392..4e1add6 100644 --- a/src/shared/lib/utils.ts +++ b/src/shared/lib/utils.ts @@ -13,6 +13,34 @@ export function formatDate(date: string | Date, locale: string = "zh-CN") { }).format(new Date(date)) } +/** + * 格式化日期+时间(含小时、分钟)。 + * 用于替代各处重复的 `new Date(x).toLocaleString("zh-CN", {...})` 调用。 + */ +export function formatDateTime(date: string | Date, locale: string = "zh-CN") { + return new Intl.DateTimeFormat(locale, { + year: "numeric", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + }).format(new Date(date)) +} + +/** + * 格式化为长日期(含星期、完整月份名)。 + * 用于替代 `new Date(x).toLocaleDateString("en-US", { weekday: "long", year: "numeric", month: "long", day: "numeric" })`。 + * 默认使用中文 locale,输出形如「2026年6月20日 周一」。 + */ +export function formatLongDate(date: string | Date, locale: string = "zh-CN") { + return new Intl.DateTimeFormat(locale, { + weekday: "short", + year: "numeric", + month: "long", + day: "numeric", + }).format(new Date(date)) +} + /** Next.js App Router 搜索参数类型 */ export type SearchParams = { [key: string]: string | string[] | undefined }