feat(textbooks): 教材模块审计重构 — 跨模块解耦 + 权限 + i18n + 错误边界 + 纯函数抽取

P0 修复:
- 解耦跨模块 UI 依赖:knowledge-point-dialogs 不再直接 import questions,
  改为 renderQuestionCreator render prop 由页面注入
- 接入 usePermission Hook 替换 canEdit 硬编码
- 全模块 i18n 改造:新增 en/zh-CN 翻译文件,替换所有硬编码文案
- Server Action 资源归属校验:新增 verifyChapterBelongsToTextbook/
  verifyKnowledgePointBelongsToTextbook,在 reorder/update/delete/create 中校验

P1 改进:
- 补齐 Error Boundary:4 个 error.tsx + TextbookSectionErrorBoundary 区块包裹
- 抽取纯函数到 utils.ts/graph-layout.ts/constants.ts 并补单测(26 用例全通过)
- 消除重复组件:删除 knowledge-point-panel/create-knowledge-point-dialog
- 修复类型断言:chapter.children! → 守卫式访问
- 图谱 a11y:添加 role/aria-label/aria-pressed
- 统一删除确认:confirm() → AlertDialog
- 数据范围过滤:getTextbooksWithScope 支持学生端按年级过滤

P2 预留:
- TextbookAnalytics 埋点接口 + Provider + Hook

同步 005 架构数据 JSON:补充 getTextbooksWithScope/verify*/ChapterTreeNode 等
This commit is contained in:
SpecialX
2026-06-22 16:25:59 +08:00
parent 45ee1ae43c
commit 22d3f07fcf
35 changed files with 2043 additions and 792 deletions

View File

@@ -0,0 +1,186 @@
{
"list": {
"title": "教材",
"subtitle": "管理数字课程资源与章节。",
"add": "新建教材",
"empty": {
"withFilters": "没有匹配的教材",
"withoutFilters": "暂无教材",
"withFiltersDesc": "请尝试清除筛选或调整关键词。",
"withoutFiltersDesc": "创建你的第一本教材以开始组织章节。"
},
"clearFilters": "清除筛选",
"chapters": "章节"
},
"student": {
"list": {
"title": "教材",
"subtitle": "浏览你的课程教材。",
"empty": {
"withFilters": "没有匹配的教材",
"withoutFilters": "暂无教材",
"withFiltersDesc": "请尝试清除筛选或调整关键词。",
"withoutFiltersDesc": "暂时没有可用的教材。"
}
},
"noUser": "未找到用户",
"noUserDesc": "请创建学生用户以查看教材。"
},
"reader": {
"back": "返回教材列表",
"tabs": {
"chapters": "章节目录",
"knowledge": "知识点",
"graph": "图谱"
},
"selectChapter": "请选择一个章节开始阅读。",
"selectChapterKnowledge": "请选择一个章节查看知识点。",
"selectChapterGraph": "请选择一个章节查看知识图谱。",
"emptyKnowledge": "该章节暂无知识点。",
"emptyContent": "暂无内容",
"editContent": "编辑内容",
"cancel": "取消",
"save": "保存",
"saving": "保存中...",
"addKnowledgePoint": "添加知识点",
"clickToViewKp": "点击查看知识点详情",
"noChapters": "暂无章节",
"noChaptersDesc": "这本教材还没有章节。"
},
"dialog": {
"create": {
"title": "新建教材",
"description": "创建一本新的数字教材。完成后点击保存。",
"submit": "保存",
"saving": "保存中..."
},
"settings": {
"title": "教材设置",
"description": "更新教材信息或删除此教材。",
"delete": "删除教材",
"deleteConfirmTitle": "确认删除教材?",
"deleteConfirmDesc": "此操作无法撤销。将永久删除该教材及其所有章节和知识点。",
"save": "保存修改",
"processing": "处理中...",
"trigger": "设置"
},
"chapter": {
"createTitle": "新建章节",
"createDesc": "创建一个新章节或小节。",
"submit": "创建章节",
"creating": "创建中...",
"deleteTitle": "删除章节?",
"deleteDesc": "将永久删除 {title}。此操作无法撤销。",
"delete": "删除",
"deleting": "删除中...",
"cannotDeleteWithSubchapters": "无法删除含有子章节的章节",
"addSubchapter": "添加子章节",
"titlePlaceholder": "例如:第一章:入门"
},
"knowledge": {
"createTitle": "添加知识点",
"createDesc": "从选中的文本创建知识点。",
"editTitle": "编辑知识点",
"editDesc": "修改知识点的名称和描述。",
"name": "名称",
"description": "描述(可选)",
"descriptionPlaceholder": "请输入描述...",
"displayName": "显示名称",
"anchorText": "高级:关联文本(影响文中高亮)",
"anchorTextHint": "修改此字段会改变文中被高亮匹配的文字。通常保持与原文一致。",
"create": "创建",
"creating": "创建中...",
"save": "保存",
"saving": "保存中...",
"cancel": "取消",
"deleteTitle": "确认删除",
"deleteDesc": "确定要删除这个知识点吗?此操作无法撤销。",
"delete": "删除",
"createQuestion": "创建相关题目",
"editKp": "编辑知识点",
"deleteKp": "删除知识点"
}
},
"field": {
"title": "标题",
"subject": "学科",
"grade": "年级",
"publisher": "出版社",
"titlePlaceholder": "例如:高等数学",
"publisherPlaceholder": "例如:人教社",
"subjectPlaceholder": "选择学科",
"gradePlaceholder": "选择年级"
},
"filters": {
"searchPlaceholder": "按标题、出版社搜索...",
"allSubjects": "全部学科",
"allGrades": "全部年级"
},
"card": {
"chapters": "章节",
"updated": "更新于",
"gradeNA": "暂无年级",
"publisherNA": "暂无出版社",
"editContent": "编辑内容",
"delete": "删除",
"moreOptions": "更多操作"
},
"panel": {
"knowledgePoints": "知识点",
"noPointsYet": "暂无知识点",
"noPointsDesc": "添加知识点以标记本章内容。",
"selectChapter": "选择一个章节以管理知识点",
"level": "等级"
},
"subject": {
"mathematics": "数学",
"physics": "物理",
"chemistry": "化学",
"biology": "生物",
"english": "英语",
"history": "历史",
"geography": "地理"
},
"grade": {
"grade7": "七年级",
"grade8": "八年级",
"grade9": "九年级",
"grade10": "高一",
"grade11": "高二",
"grade12": "高三"
},
"error": {
"loadFailed": "教材加载失败",
"loadFailedDesc": "加载教材内容时发生错误,请重试。",
"retry": "重试"
},
"action": {
"createSuccess": "教材创建成功。",
"createFailed": "创建教材失败。",
"updateSuccess": "教材更新成功。",
"updateFailed": "更新教材失败。",
"deleteSuccess": "教材删除成功。",
"deleteFailed": "删除教材失败。",
"chapterCreateSuccess": "章节创建成功",
"chapterCreateFailed": "创建章节失败",
"chapterDeleteSuccess": "章节删除成功",
"chapterDeleteFailed": "删除章节失败",
"contentUpdateSuccess": "内容更新成功",
"contentUpdateFailed": "更新内容失败",
"kpCreateSuccess": "知识点创建成功",
"kpCreateFailed": "创建知识点失败",
"kpUpdateSuccess": "知识点更新成功",
"kpUpdateFailed": "更新知识点失败",
"kpDeleteSuccess": "知识点删除成功",
"kpDeleteFailed": "删除知识点失败",
"reorderSuccess": "排序已更新",
"reorderFailed": "章节排序失败",
"fillRequired": "请填写所有必填字段。",
"titleRequired": "标题为必填项",
"nameRequired": "名称为必填项",
"invalidContent": "章节内容数据无效",
"errorOccurred": "发生错误",
"deleteFailed": "删除失败",
"updateFailedGeneric": "更新失败"
}
}