feat(shared): update db schema, i18n messages, rate-limit, auth-session-provider

- Update src/shared/db/schema.ts

- Update i18n messages for en and zh-CN (error-book, exam-homework, leave,

  lesson-preparation, notifications, rbac, student, textbooks, diagnostic)

- Update src/shared/lib/rate-limit/index.ts and redis-limiter.ts

- Update src/shared/components/auth-session-provider.tsx
This commit is contained in:
SpecialX
2026-07-04 10:23:29 +08:00
parent 3569d83b8e
commit d2c250a1b3
21 changed files with 848 additions and 331 deletions

View File

@@ -2,10 +2,17 @@
import * as React from "react"
import { SessionProvider } from "next-auth/react"
import type { Session } from "next-auth"
export function AuthSessionProvider({ children }: { children: React.ReactNode }) {
interface AuthSessionProviderProps {
children: React.ReactNode
/** SSR 期间获取的 session避免 client 端异步获取导致的 hydration mismatch */
session?: Session | null
}
export function AuthSessionProvider({ children, session }: AuthSessionProviderProps) {
return (
<SessionProvider refetchOnWindowFocus={false} refetchInterval={0}>
<SessionProvider session={session} refetchOnWindowFocus={false} refetchInterval={0}>
{children}
</SessionProvider>
)

View File

@@ -1587,8 +1587,9 @@ export const examProctoringEvents = mysqlTable("exam_proctoring_events", {
export const knowledgePointMastery = mysqlTable("knowledge_point_mastery", {
id: id("id").primaryKey(),
studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
knowledgePointId: varchar("knowledge_point_id", { length: 128 }).notNull().references(() => knowledgePoints.id, { onDelete: "cascade" }),
// 显式 foreignKey 见表参数(避免自动生成 FK 名过长 > 64 字符)
studentId: varchar("student_id", { length: 128 }).notNull(),
knowledgePointId: varchar("knowledge_point_id", { length: 128 }).notNull(),
masteryLevel: decimal("mastery_level", { precision: 5, scale: 2 }).default("0").notNull(),
totalQuestions: int("total_questions").default(0).notNull(),
correctQuestions: int("correct_questions").default(0).notNull(),
@@ -1599,6 +1600,16 @@ export const knowledgePointMastery = mysqlTable("knowledge_point_mastery", {
studentKpPk: primaryKey({ columns: [table.studentId, table.knowledgePointId] }),
studentIdx: index("mastery_student_idx").on(table.studentId),
kpIdx: index("mastery_kp_idx").on(table.knowledgePointId),
studentFk: foreignKey({
columns: [table.studentId],
foreignColumns: [users.id],
name: "kpm_student_fk",
}).onDelete("cascade"),
kpFk: foreignKey({
columns: [table.knowledgePointId],
foreignColumns: [knowledgePoints.id],
name: "kpm_kp_fk",
}).onDelete("cascade"),
}));
export const diagnosticReportStatusEnum = mysqlEnum("report_status", ["draft", "published", "archived"]);
@@ -1750,6 +1761,28 @@ export const lessonPlanSubstitutes = mysqlTable("lesson_plan_substitutes", {
dateRangeIdx: index("lps_date_range_idx").on(table.startDate, table.endDate),
}));
// V5-7课案-课时绑定表(将课案绑定到具体班级的某个日期/节次)
export const lessonPlanSchedules = mysqlTable("lesson_plan_schedules", {
id: id("id").primaryKey(),
planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }),
classId: varchar("class_id", { length: 128 }).notNull().references(() => classes.id, { onDelete: "cascade" }),
/** 排课日期 YYYY-MM-DD */
scheduledDate: date("scheduled_date").notNull(),
/** 节次序号1-12对应学校节次安排 */
period: int("period").notNull(),
/** 关联 class_schedule.id如使用课表*/
classScheduleId: varchar("class_schedule_id", { length: 128 }),
/** 教学时长(分钟),默认 40 */
durationMin: int("duration_min").default(40).notNull(),
createdBy: varchar("created_by", { length: 128 }).notNull().references(() => users.id),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
planIdx: index("lpsc_plan_idx").on(table.planId),
classDateIdx: index("lpsc_class_date_idx").on(table.classId, table.scheduledDate),
teacherDateIdx: index("lpsc_plan_date_idx").on(table.planId, table.scheduledDate),
}));
// --- 25. System Settings (系统设置 - 键值对存储) ---
export const systemSettings = mysqlTable("system_settings", {
@@ -2115,7 +2148,8 @@ export const lessonPlanFormativeItems = mysqlTable("lesson_plan_formative_items"
/** M5 学生作答记录 */
export const lessonPlanFormativeResponses = mysqlTable("lesson_plan_formative_responses", {
id: id("id").primaryKey(),
itemId: varchar("item_id", { length: 128 }).notNull().references(() => lessonPlanFormativeItems.id, { onDelete: "cascade" }),
// 显式 foreignKey 见表参数(避免自动生成 FK 名过长 > 64 字符)
itemId: varchar("item_id", { length: 128 }).notNull(),
studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
classId: varchar("class_id", { length: 128 }),
/** 学生作答JSON */
@@ -2129,6 +2163,11 @@ export const lessonPlanFormativeResponses = mysqlTable("lesson_plan_formative_re
itemIdx: index("lpfr_item_idx").on(table.itemId),
studentIdx: index("lpfr_student_idx").on(table.studentId),
classIdx: index("lpfr_class_idx").on(table.classId),
itemFk: foreignKey({
columns: [table.itemId],
foreignColumns: [lessonPlanFormativeItems.id],
name: "lpfr_item_fk",
}).onDelete("cascade"),
}));
// --- 31. Lesson Plan Analytics (M10 备课分析仪表盘) ---

View File

@@ -283,7 +283,7 @@
"reviewRecorded": "Review recorded",
"archived": "Error archived",
"deleted": "Error deleted",
"collected": "Collected {{count}} errors",
"collected": "Collected {count} errors",
"noNewErrors": "No new errors to collect",
"addFailed": "Failed to add error",
"saveFailed": "Save failed",

View File

@@ -70,7 +70,7 @@
"3": "Medium",
"4": "Med-Hard",
"5": "Hard",
"ariaLabel": "Difficulty level {{level}}: {{label}}"
"ariaLabel": "Difficulty level {level}: {label}"
},
"actions": {
"preview": "Preview Exam",
@@ -83,7 +83,7 @@
"archive": "Archive",
"delete": "Delete",
"deleteConfirmTitle": "Are you absolutely sure?",
"deleteConfirmDescription": "This action cannot be undone. This will permanently delete the exam \"{{title}}\" and remove all associated data.",
"deleteConfirmDescription": "This action cannot be undone. This will permanently delete the exam \"{title}\" and remove all associated data.",
"cancel": "Cancel",
"deleteSuccess": "Exam deleted successfully",
"deleteFailed": "Failed to delete exam",
@@ -165,7 +165,7 @@
"richEditorHint": "Switch to rich text editor",
"loaded": "loaded",
"startHint": "Start by adding questions from the right panel",
"itemsInStructure": "{{count}} items in structure",
"itemsInStructure": "{count} items in structure",
"saveSuccess": "Exam draft saved",
"saveFailed": "Save failed",
"publishSuccess": "Published exam",
@@ -280,7 +280,7 @@
"sectionLabel": "Section",
"partLabel": "Part",
"groupLabel": "Group",
"questionCountSummary": "({{count}} questions, {{score}} pts)",
"questionCountSummary": "({count} questions, {score} pts)",
"typeSingleChoice": "Single Choice",
"typeMultipleChoice": "Multiple Choice",
"typeJudgment": "Judgment",
@@ -310,8 +310,8 @@
"taskInterrupted": "Task was interrupted after page refresh, please regenerate",
"untitledExam": "Untitled Exam",
"queuedSuccess": "Added to background queue, you can continue editing",
"backgroundComplete": "Background generation complete: {{title}}",
"backgroundFailed": "Background generation failed: {{title}}",
"backgroundComplete": "Background generation complete: {title}",
"backgroundFailed": "Background generation failed: {title}",
"selectQuestionFirst": "Please select a question first",
"questionNotFound": "Selected question not found",
"enterRewriteInstruction": "Please enter rewrite instruction",
@@ -321,7 +321,7 @@
"pasteSourceFirst": "Please paste the full exam text first"
},
"paperPreview": {
"scoreWithUnit": "({{score}} pts)"
"scoreWithUnit": "({score} pts)"
},
"actionMessages": {
"enterRewriteInstruction": "Please enter rewrite instruction",
@@ -361,16 +361,16 @@
"noQuestionText": "(no question text)"
},
"card": {
"level": "Lvl {{level}}",
"minutes": "{{count}} min",
"points": "{{count}} pts",
"questions": "{{count}} Questions"
"level": "Lvl {level}",
"minutes": "{count} min",
"points": "{count} pts",
"questions": "{count} Questions"
},
"viewer": {
"section": "Section",
"group": "Group",
"score": "Score",
"scoreLabel": "Score: {{score}}",
"scoreLabel": "Score: {score}",
"noQuestions": "No questions available.",
"unknown": "unknown"
},
@@ -379,7 +379,7 @@
"title": "Exam Preview",
"generating": "Generating preview...",
"fullPreview": "Full exam preview",
"summary": "{{count}} questions · {{subject}} · {{grade}} · {{minutes}} min · {{total}} pts",
"summary": "{count} questions · {subject} · {grade} · {minutes} min · {total} pts",
"noPreview": "No preview available",
"confirmCreate": "Confirm & Create",
"untitledQuestion": "Untitled question",
@@ -390,7 +390,7 @@
"label": "Options",
"addOption": "Add option",
"correct": "Correct",
"markCorrectAria": "Mark option {{id}} as correct answer",
"markCorrectAria": "Mark option {id} as correct answer",
"deleteOptionAria": "Delete option"
},
"editorExtensions": {
@@ -400,7 +400,7 @@
"group": {
"titlePlaceholder": "Group title (e.g. I. Multiple Choice)",
"instructionPlaceholder": "Instruction (e.g. 3 pts each, 24 pts total) — optional, total auto-calculated",
"statsSummary": "{{count}} questions · {{score}} pts"
"statsSummary": "{count} questions · {score} pts"
},
"question": {
"typeSingleChoice": "Single Choice",
@@ -416,7 +416,7 @@
"levelVolume": "Volume",
"levelPart": "Part",
"levelSubVolume": "Sub-volume",
"statsSummary": "{{count}} questions · {{score}} pts"
"statsSummary": "{count} questions · {score} pts"
}
}
},
@@ -430,7 +430,7 @@
"emptyDescription": "You haven't created any assignments yet.",
"emptyFilteredDescription": "Try clearing filters or adjusting keywords.",
"clearFilters": "Clear filters",
"filterByClass": "Filter by class: {{className}}",
"filterByClass": "Filter by class: {className}",
"columns": {
"title": "Title",
"status": "Status",
@@ -488,7 +488,7 @@
},
"take": {
"questions": "Questions",
"question": "Question {{index}}",
"question": "Question {index}",
"points": "points",
"startAssignment": "Start Assignment",
"submitAssignment": "Submit Assignment",
@@ -505,12 +505,12 @@
"readyDescription": "Click the \"Start Assignment\" button above to begin. Your answers will be saved when you click \"Save Answer\".",
"startNow": "Start Now",
"back": "Back",
"timedExam": "Timed exam: {{minutes}} minutes",
"timedExam": "Timed exam: {minutes} minutes",
"timeRemaining": "Time remaining",
"timeUpAutoSubmit": "Time is up, auto-submitting...",
"confirmSubmit": "Confirm Submission",
"confirmSubmitDescription": "All questions have been answered. Submitted answers cannot be changed. Are you sure you want to submit?",
"unansweredWarning": "You have {{count}} unanswered question(s). Submitted answers cannot be changed. Are you sure you want to submit?",
"unansweredWarning": "You have {count} unanswered question(s). Submitted answers cannot be changed. Are you sure you want to submit?",
"cancel": "Cancel",
"confirmSubmitAction": "Confirm Submit",
"submitSuccess": "Submitted",
@@ -521,15 +521,15 @@
"status": "Status",
"dueDate": "Due Date",
"overdue": "Overdue",
"hoursLeft": "{{hours}} hour(s) left",
"hoursLeft": "{hours} hour(s) left",
"lessThanOneHour": "Less than 1 hour left",
"attempts": "Attempts",
"attemptsUsed": "{{used}} / {{max}} used",
"attemptsRemaining": {{remaining}} remaining",
"attemptsUsed": "{used} / {max} used",
"attemptsRemaining": "· {remaining} remaining",
"description": "Description",
"noDescription": "No description provided.",
"progress": "Progress",
"jumpToQuestion": "Jump to question {{index}}",
"jumpToQuestion": "Jump to question {index}",
"answered": "Answered",
"unanswered": "Not answered",
"yourAnswer": "Your answer",
@@ -550,12 +550,12 @@
"scanDescription": "After answering on paper, photograph and upload the full paper",
"selectImageFiles": "Please select image files",
"uploadFailed": "Upload failed",
"uploadSuccess": "Uploaded {{count}} images",
"uploadSuccess": "Uploaded {count} images",
"deleteScan": "Delete",
"moveUp": "Move Up",
"moveDown": "Move Down",
"dragDropHint": "Drag images here, or click to select files",
"pageLabel": "Page {{page}}",
"pageLabel": "Page {page}",
"scanDisabled": "Submitted, scan images cannot be modified"
},
"grade": {
@@ -589,7 +589,7 @@
"scoreLabel": "Score",
"addFeedback": "Add Feedback",
"hideFeedback": "Hide Feedback",
"feedbackPlaceholder": "Provide feedback for {{name}}...",
"feedbackPlaceholder": "Provide feedback for {name}...",
"submitGrades": "Submit Grades",
"saving": "Saving...",
"gradesSaved": "Grading saved successfully",
@@ -600,7 +600,7 @@
"next": "Next",
"gradesAutoSaveNote": "Grades are saved automatically when you click Submit. Students will see their grades and feedback immediately after you submit.",
"batchAutoGrade": "Batch Auto-Grade",
"batchSelected": "{{count}} submissions selected",
"batchSelected": "{count} submissions selected",
"batchSelectAtLeastOne": "Please select at least one submission",
"batchFailed": "Batch grading failed",
"selectAll": "Select All",
@@ -616,9 +616,9 @@
"noScans": "No scan images",
"saveFailed": "Save failed",
"scanFeedbackPlaceholder": "Grading feedback (optional)...",
"scoreOutOf": "/ {{max}} pts",
"questionsCount": "Questions & Grading ({{count}} items)",
"scanPagesCount": "Student Scan Images ({{count}} pages)"
"scoreOutOf": "/ {max} pts",
"questionsCount": "Questions & Grading ({count} items)",
"scanPagesCount": "Student Scan Images ({count} pages)"
},
"review": {
"title": "Review",
@@ -700,15 +700,15 @@
"questionPreview": "Question Preview",
"errorAnalysis": "Error Analysis",
"errorRateOverview": "Error Rate Overview",
"errorRateAriaLabel": "Error rate {{rate}}%",
"errorRateAriaLabel": "Error rate {rate}%",
"question": "Question",
"errors": "Errors",
"errorRateLabel": "Error Rate",
"wrongAnswersWithCount": "Wrong Answers ({{count}})",
"wrongAnswersWithCount": "Wrong Answers ({count})",
"wrongAnswers": "Wrong Answers",
"noWrongAnswers": "No wrong answers recorded.",
"studentAnswer": "Student Answer",
"studentCount": "{{count}} student(s)",
"studentCount": "{count} student(s)",
"notAnswered": "Not answered",
"selectQuestionHint": "Select a question from the left",
"selectQuestionHintDesc": "to view error analysis",
@@ -721,9 +721,9 @@
"zoomIn": "Zoom in",
"rotate": "Rotate",
"fullscreen": "Fullscreen",
"pageIndicator": "Page {{current}} / {{total}}",
"answerImageAlt": "Answer image page {{page}}",
"thumbnailAlt": "Thumbnail {{page}}"
"pageIndicator": "Page {current} / {total}",
"answerImageAlt": "Answer image page {page}",
"thumbnailAlt": "Thumbnail {page}"
},
"submissions": {
"title": "Submissions",
@@ -744,26 +744,26 @@
},
"excellent": {
"title": "Excellent Submissions",
"description": "Top submissions scoring {{minPercentage}}% or above in this assignment.",
"description": "Top submissions scoring {minPercentage}% or above in this assignment.",
"empty": "No excellent submissions yet.",
"emptyHint": "They will appear here after grading is complete.",
"loading": "Loading excellent submissions...",
"loadFailed": "Failed to load",
"retry": "Retry",
"rank": "Rank {{rank}}",
"rank": "Rank {rank}",
"scoreLabel": "Score",
"scoreValue": "{{score}} / {{max}}",
"percentage": "{{value}}%",
"scoreValue": "{score} / {max}",
"percentage": "{value}%",
"lateTag": "Late",
"viewDetail": "View Details",
"submittedAt": "Submitted on {{date}}",
"submittedAt": "Submitted on {date}",
"studentAnon": "Student"
},
"parentExam": {
"examsTaken": "Exams Taken",
"averageScore": "Average Score",
"bestScore": "Best Score",
"examResults": "{{name}}'s Exam Results",
"examResults": "{name}'s Exam Results",
"examResultsDescription": "Recent exam scores and performance trends",
"noResults": "No exam results",
"noResultsHint": "Exam results will appear here once available.",

View File

@@ -1,12 +1,16 @@
{
"title": "Leave Request",
"title.parent": "Online Leave",
"title.teacher": "Leave Approval",
"title.student": "My Leave Requests",
"description": "Submit a leave request for your child.",
"description.parent": "Submit a leave request for your child. Attendance is auto-synced after homeroom teacher approval.",
"description.teacher": "View and approve leave requests from students in your classes. Approved leaves automatically mark attendance as 'excused'.",
"description.student": "Submit your own leave request. It takes effect after homeroom teacher approval.",
"title": {
"default": "Leave Request",
"parent": "Online Leave",
"teacher": "Leave Approval",
"student": "My Leave Requests"
},
"description": {
"default": "Submit a leave request for your child.",
"parent": "Submit a leave request for your child. Attendance is auto-synced after homeroom teacher approval.",
"teacher": "View and approve leave requests from students in your classes. Approved leaves automatically mark attendance as 'excused'.",
"student": "Submit your own leave request. It takes effect after homeroom teacher approval."
},
"backToDashboard": "Back to Dashboard",
"onlineLeave": "Online Leave Request",
"comingSoon": "Coming soon",

View File

@@ -27,7 +27,12 @@
"unpublishPlanConfirm": "Students and parents will no longer be able to view this lesson plan after unpublishing. Confirm?",
"publishPlanSuccess": "Lesson plan published",
"unpublishPlanSuccess": "Lesson plan unpublished",
"viewHomework": "View"
"viewHomework": "View",
"undo": "Undo",
"undoShortcut": "Undo (Ctrl+Z)",
"redo": "Redo",
"redoShortcut": "Redo (Ctrl+Shift+Z)",
"scheduleLesson": "Schedule"
},
"status": {
"saving": "Saving...",
@@ -39,7 +44,15 @@
"published": "Published",
"rejected": "Rejected",
"archived": "Archived",
"publishedAsHomework": "Published as Homework"
"publishedAsHomework": "Published as Homework",
"retrySave": "Retry Save",
"saveFailed": "Save Failed",
"saveFailedHint": "Please check your network and retry. Edits are preserved.",
"recovered": "Save recovered",
"offline": "Network offline",
"offlineHint": "Edits are kept locally and will auto-save when network restores.",
"offlineBadge": "Offline",
"backOnline": "Back online"
},
"blockType": {
"objective": "Objective",
@@ -130,7 +143,46 @@
"textbookLabel": "Textbook",
"chapterLabel": "Chapter",
"selectNodeForAnchor": "Select a node to link",
"createNewNode": "Create and link a new node"
"createNewNode": "Create and link a new node",
"autoLayout": "Auto Layout",
"autoLayoutHint": "Auto-arrange nodes based on flow relations",
"stageLabel": "Stage",
"stageNone": "Uncategorized",
"stage": {
"import": "Introduction",
"new_teaching": "New Teaching",
"consolidation": "Consolidation",
"summary": "Summary"
},
"differentiationLabel": "Differentiation",
"differentiationNone": "None",
"differentiation": {
"basic": "Basic",
"intermediate": "Intermediate",
"advanced": "Advanced"
},
"consistencyTitle": "Alignment",
"consistencyScore": "Alignment score: {score}",
"consistencyCoverage": "Objective coverage: {covered}/{total}",
"consistencyOpen": "Alignment Check",
"consistencyClose": "Close",
"consistencyNoIssues": "No alignment issues found",
"consistencyObjectiveCount": "Objectives {count}",
"consistencyExerciseCount": "Assessments {count}"
},
"consistency": {
"title": "Teaching-Assessment Alignment Check",
"score": "Alignment score: {score}",
"coverage": "Objective coverage: {covered}/{total}",
"noIssues": "No alignment issues found",
"code": {
"noObjective": "Missing objective node (consider adding an objective node)",
"noExercise": "Missing assessment node (consider adding an exercise node)",
"objectiveNotAssessed": "Objective \"{title}\" is not covered by any assessment",
"exerciseNoKnowledgePoint": "Assessment \"{title}\" has no linked knowledge points",
"exerciseWithoutObjective": "Assessment \"{title}\" is not linked to any objective",
"objectiveNoKnowledgePoint": "Objective \"{title}\" has no tagged knowledge points"
}
},
"readonly": {
"title": "View Lesson Plan",
@@ -160,6 +212,65 @@
"title": "Grade Lesson Plans",
"description": "View lesson plans from teachers in your grade"
},
"library": {
"title": "School Lesson Plan Library",
"description": "Browse outstanding lesson plans shared by teachers in your school",
"empty": "No reference lesson plans available",
"fork": "Copy to my plans",
"forkSuccess": "Copied to your lesson plans",
"byCreator": "By: {creator}",
"noCreator": "Unknown author"
},
"feedback": {
"title": "AI Teaching Feedback",
"open": "AI Feedback",
"loading": "AI is analyzing the lesson plan...",
"loadFailed": "AI feedback generation failed, please retry later",
"empty": "No feedback suggestions generated",
"summary": "Overall Assessment",
"score": "Score",
"category": {
"strengths": "Strengths",
"improvements": "Improvements",
"alignment": "Alignment",
"differentiation": "Differentiation"
}
},
"aiDifferentiation": {
"title": "AI Differentiation & Curriculum Check",
"open": "AI Differentiation",
"loading": "AI is analyzing...",
"loadFailed": "AI analysis failed, please try again later",
"empty": "No results generated",
"noKnowledgePoints": "No knowledge points provided, curriculum check unavailable",
"covered": "{count} covered",
"missed": "{count} missed",
"tabs": {
"differentiation": "Differentiation",
"curriculum": "Curriculum Check",
"assessment": "Explainable Assessment"
},
"level": {
"basic": "Basic",
"intermediate": "Intermediate",
"advanced": "Advanced"
}
},
"heatmap": {
"title": "Curriculum Coverage Heatmap",
"description": "View how your lesson plans cover textbook knowledge points",
"totalKps": "Total KPs",
"coveredKps": "Covered",
"coverageRate": "Coverage Rate",
"blindSpotTitle": "{count} blind spots",
"andMore": " and more",
"unknownChapter": "Unknown Chapter",
"chapterCoverage": "{covered}/{total} ({rate}%)",
"notCovered": "Not covered",
"planCount": "{count} plans",
"selectTextbook": "Select a textbook to view heatmap",
"noData": "No knowledge points for this textbook"
},
"picker": {
"textbookLabel": "Select Textbook",
"chapterLabel": "Select Chapter",
@@ -172,7 +283,11 @@
"noChapters": "No chapters in this textbook",
"selectedChapter": "Selected: {chapter}",
"skeletonHint": "A lesson skeleton centered on the textbook content will be generated (10 teaching nodes)",
"errorTextbookChapterRequired": "Please select a textbook and chapter"
"errorTextbookChapterRequired": "Please select a textbook and chapter",
"searchTextbookPlaceholder": "Search textbooks (title/subject/grade)...",
"searchTextbookLabel": "Search textbooks",
"recentSection": "Recently Used",
"searchEmpty": "No matching textbooks"
},
"filters": {
"searchPlaceholder": "Search title...",
@@ -203,6 +318,25 @@
"autoLabel": "Auto version",
"revertLabel": "Revert to v{versionNo}"
},
"diff": {
"summary": "Added {added} · Removed {removed} · Modified {modified}",
"comparing": "Comparing v{version} with current",
"noChanges": "No changes between the two versions",
"unchangedCount": "{count} nodes unchanged",
"added": "Added",
"removed": "Removed",
"modified": "Modified",
"unchanged": "Unchanged",
"changedFields": "Changed fields",
"compareWithCurrent": "Compare with current",
"back": "Back",
"field": {
"title": "Title",
"stage": "Stage",
"differentiation": "Differentiation",
"data": "Content"
}
},
"knowledgePoint": {
"title": "Select Knowledge Points",
"empty": "No knowledge points found. Please create them in the textbook module first.",
@@ -232,6 +366,10 @@
"questionId": "Question {id}",
"type": {
"single_choice": "Single Choice",
"multiple_choice": "Multiple Choice",
"true_false": "True/False",
"short_answer": "Short Answer",
"essay": "Essay",
"text": "Fill in Blank",
"judgment": "True/False"
},
@@ -246,7 +384,8 @@
"difficultyLabel": "Difficulty",
"knowledgePointLabel": "Knowledge Points",
"stemRequired": "Please enter the question stem",
"addBtn": "Add"
"addBtn": "Add",
"noDetail": "(no detail available)"
},
"exercise": {
"purposeLabel": "Purpose",
@@ -271,7 +410,27 @@
"planNotFound": "Lesson plan not found",
"noPermission": "No permission to publish",
"homeworkTitle": "{title} - Homework",
"homeworkDescription": "From lesson plan"
"homeworkDescription": "From lesson plan",
"step1": "Select Classes",
"step2": "Preview Homework",
"step3": "Confirm Publish",
"stepIndicator": "Step {current}/{total}: {label}",
"next": "Next",
"back": "Back",
"previewClassCount": "Target Classes",
"previewQuestionCount": "Questions",
"previewTotalScore": "Total Score",
"previewQuestionList": "Question List",
"previewSource": "Source: {source}",
"previewNoStem": "(no stem preview)",
"previewScore": "{score} pts",
"confirmTitle": "Please confirm publish details",
"confirmClassCount": "Target classes: {count}",
"confirmQuestionCount": "Questions: {count}",
"confirmTotalScore": "Total score: {score}",
"confirmAvailableAt": "Available at: {time}",
"confirmDueAt": "Due at: {time}",
"confirmWarning": "Students and parents will receive the homework immediately upon publishing. Please verify carefully."
},
"textStudy": {
"sourceTextLabel": "Source Text",
@@ -336,7 +495,12 @@
"text": "Text-based"
},
"contentLabel": "Board Content",
"contentPlaceholder": "Enter board content..."
"contentPlaceholder": "Enter board content...",
"editMode": "Edit",
"previewMode": "Preview",
"editHint": "Tip: Use 2 spaces or 1 Tab to indent levels",
"previewEmpty": "No content yet, switch to edit mode to enter",
"untitled": "Central Topic"
},
"import": {
"hint": "Design lesson introduction",
@@ -365,6 +529,45 @@
"richText": {
"placeholder": "Enter content..."
},
"export": {
"title": "Export / Print",
"button": "Export",
"print": "Print",
"variantDetailed": "Detailed",
"variantConcise": "Concise",
"teacher": "Teacher",
"class": "Class",
"totalDuration": "Duration {count} min",
"lastSavedAt": "Last saved",
"empty": "Lesson plan is empty",
"emptySection": "(empty)",
"footerHint": "Lesson plan export - {variant}"
},
"attachment": {
"title": "Attachment Library",
"add": "Upload File",
"insert": "Insert Attachment",
"libraryLabel": "Attachments in this lesson plan",
"empty": "No attachments yet. Upload one.",
"delete": "Delete",
"remove": "Remove",
"embeddedCount": "{count} attachments embedded",
"createSuccess": "Attachment added",
"deleteSuccess": "Attachment deleted",
"uploadFailed": "Upload failed",
"uploadSuccess": "Upload succeeded",
"type": {
"reference": "Reference",
"material": "Material",
"supplementary": "Supplementary"
},
"kind": {
"image": "Image",
"audio": "Audio",
"video": "Video",
"file": "File"
}
},
"error": {
"getList": "Failed to get lesson plan list",
"getOne": "Failed to get lesson plan",
@@ -394,7 +597,12 @@
"titleTooLong": "Title cannot exceed 255 characters",
"templateRequired": "Please select a template",
"invalidDate": "Invalid date format",
"classRequired": "At least one class is required"
"classRequired": "At least one class is required",
"invalidInput": "Invalid input data",
"unauthorized": "Unauthorized operation",
"planIdRequired": "Plan ID is required",
"classIdRequired": "Class ID is required",
"dateRequired": "Date is required"
},
"confirm": {
"archive": "Archive this lesson plan?",
@@ -496,26 +704,6 @@
"recordSuccess": "Response recorded",
"recordFailed": "Failed to record response"
},
"attachment": {
"title": "Attachments",
"add": "Add Attachment",
"edit": "Edit",
"delete": "Delete",
"noAttachments": "No attachments",
"name": "Name",
"type": "Type",
"size": "Size",
"url": "URL",
"upload": "Upload",
"download": "Download",
"deleteConfirm": "Are you sure you want to delete this attachment?",
"deleteSuccess": "Attachment deleted",
"deleteFailed": "Failed to delete attachment",
"createSuccess": "Attachment added",
"createFailed": "Failed to add attachment",
"updateSuccess": "Attachment updated",
"updateFailed": "Failed to update attachment"
},
"substitute": {
"title": "Substitute Teacher",
"add": "Add Assignment",
@@ -536,6 +724,24 @@
"updateSuccess": "Substitute assignment updated",
"updateFailed": "Failed to update substitute assignment"
},
"schedule": {
"title": "Schedule Lesson",
"boundList": "Bound Lessons",
"empty": "No bound lessons",
"addNew": "Add New Binding",
"classLabel": "Class",
"dateLabel": "Date",
"periodLabel": "Period",
"durationLabel": "Duration (min)",
"period": "Period {n}",
"duration": "{n} min",
"add": "Add",
"delete": "Delete",
"selectClass": "Please select a class",
"selectDate": "Please select a date",
"addSuccess": "Schedule binding added",
"deleteSuccess": "Schedule binding deleted"
},
"evaluation": {
"title": "AI Evaluation",
"generate": "Generate Evaluation",

View File

@@ -49,6 +49,8 @@
"error": {
"loadFailed": "Failed to load notifications",
"loadFailedDesc": "Sorry, an unexpected error occurred while loading notifications. Please try again later.",
"boundaryTitle": "Notifications section failed to load",
"boundaryDescription": "An error occurred while loading notifications. Please retry.",
"retry": "Retry"
}
}

View File

@@ -87,211 +87,211 @@
},
"exam": {
"create": "Create Exam",
"create.desc": "Allows creating new exams",
"createDesc": "Allows creating new exams",
"read": "Read Exam",
"read.desc": "Allows viewing exam list and details",
"readDesc": "Allows viewing exam list and details",
"update": "Update Exam",
"update.desc": "Allows modifying exam information",
"updateDesc": "Allows modifying exam information",
"delete": "Delete Exam",
"delete.desc": "Allows deleting exams",
"deleteDesc": "Allows deleting exams",
"duplicate": "Duplicate Exam",
"duplicate.desc": "Allows duplicating existing exams",
"duplicateDesc": "Allows duplicating existing exams",
"publish": "Publish Exam",
"publish.desc": "Allows publishing exams for students to take",
"publishDesc": "Allows publishing exams for students to take",
"ai_generate": "AI Generate Exam",
"ai_generate.desc": "Allows using AI to generate exam content",
"ai_generateDesc": "Allows using AI to generate exam content",
"submit": "Submit Exam",
"submit.desc": "Allows submitting exam responses",
"submitDesc": "Allows submitting exam responses",
"proctor": "Proctor Exam",
"proctor.desc": "Allows performing proctoring operations",
"proctorDesc": "Allows performing proctoring operations",
"proctor_read": "Read Proctoring",
"proctor_read.desc": "Allows viewing proctoring information"
"proctor_readDesc": "Allows viewing proctoring information"
},
"homework": {
"create": "Create Homework",
"create.desc": "Allows creating new homework",
"createDesc": "Allows creating new homework",
"grade": "Grade Homework",
"grade.desc": "Allows grading student homework",
"gradeDesc": "Allows grading student homework",
"submit": "Submit Homework",
"submit.desc": "Allows submitting homework"
"submitDesc": "Allows submitting homework"
},
"question": {
"create": "Create Question",
"create.desc": "Allows creating new questions",
"createDesc": "Allows creating new questions",
"read": "Read Question",
"read.desc": "Allows viewing question list and details",
"readDesc": "Allows viewing question list and details",
"update": "Update Question",
"update.desc": "Allows modifying question information",
"updateDesc": "Allows modifying question information",
"delete": "Delete Question",
"delete.desc": "Allows deleting questions"
"deleteDesc": "Allows deleting questions"
},
"textbook": {
"create": "Create Textbook",
"create.desc": "Allows creating new textbooks",
"createDesc": "Allows creating new textbooks",
"read": "Read Textbook",
"read.desc": "Allows viewing textbook list and details",
"readDesc": "Allows viewing textbook list and details",
"update": "Update Textbook",
"update.desc": "Allows modifying textbook information",
"updateDesc": "Allows modifying textbook information",
"delete": "Delete Textbook",
"delete.desc": "Allows deleting textbooks"
"deleteDesc": "Allows deleting textbooks"
},
"class": {
"create": "Create Class",
"create.desc": "Allows creating new classes",
"createDesc": "Allows creating new classes",
"read": "Read Class",
"read.desc": "Allows viewing class list and details",
"readDesc": "Allows viewing class list and details",
"update": "Update Class",
"update.desc": "Allows modifying class information",
"updateDesc": "Allows modifying class information",
"delete": "Delete Class",
"delete.desc": "Allows deleting classes",
"deleteDesc": "Allows deleting classes",
"enroll": "Enroll Students",
"enroll.desc": "Allows managing student enrollment in classes",
"enrollDesc": "Allows managing student enrollment in classes",
"schedule": "Class Scheduling",
"schedule.desc": "Allows managing class schedules"
"scheduleDesc": "Allows managing class schedules"
},
"school": {
"manage": "Manage School",
"manage.desc": "Allows managing school information",
"manageDesc": "Allows managing school information",
"grade_manage": "Manage Grades",
"grade_manage.desc": "Allows managing grade information",
"grade_manageDesc": "Allows managing grade information",
"user_manage": "Manage Users",
"user_manage.desc": "Allows managing system users"
"user_manageDesc": "Allows managing system users"
},
"user": {
"profile_update": "Update Profile",
"profile_update.desc": "Allows updating user profile"
"profile_updateDesc": "Allows updating user profile"
},
"ai": {
"chat": "AI Chat",
"chat.desc": "Allows using AI chat feature",
"chatDesc": "Allows using AI chat feature",
"configure": "AI Configure",
"configure.desc": "Allows configuring AI parameters"
"configureDesc": "Allows configuring AI parameters"
},
"settings": {
"admin": "Admin Settings",
"admin.desc": "Allows managing system settings"
"adminDesc": "Allows managing system settings"
},
"audit": {
"read": "Read Audit Logs",
"read.desc": "Allows viewing system audit logs"
"readDesc": "Allows viewing system audit logs"
},
"announcement": {
"manage": "Manage Announcements",
"manage.desc": "Allows creating, modifying, and deleting announcements",
"manageDesc": "Allows creating, modifying, and deleting announcements",
"read": "Read Announcements",
"read.desc": "Allows viewing announcement content"
"readDesc": "Allows viewing announcement content"
},
"grade_record": {
"manage": "Manage Grade Records",
"manage.desc": "Allows entering and modifying grade records",
"manageDesc": "Allows entering and modifying grade records",
"read": "Read Grade Records",
"read.desc": "Allows viewing grade records"
"readDesc": "Allows viewing grade records"
},
"file": {
"upload": "Upload File",
"upload.desc": "Allows uploading files",
"uploadDesc": "Allows uploading files",
"read": "Read File",
"read.desc": "Allows viewing files",
"readDesc": "Allows viewing files",
"delete": "Delete File",
"delete.desc": "Allows deleting files"
"deleteDesc": "Allows deleting files"
},
"course_plan": {
"manage": "Manage Course Plans",
"manage.desc": "Allows creating, modifying, and deleting course plans",
"manageDesc": "Allows creating, modifying, and deleting course plans",
"read": "Read Course Plans",
"read.desc": "Allows viewing course plans"
"readDesc": "Allows viewing course plans"
},
"attendance": {
"manage": "Manage Attendance",
"manage.desc": "Allows entering and modifying attendance records",
"manageDesc": "Allows entering and modifying attendance records",
"read": "Read Attendance",
"read.desc": "Allows viewing attendance records"
"readDesc": "Allows viewing attendance records"
},
"leave_request": {
"create": "Submit Leave Request",
"create.desc": "Allows parents/students to submit online leave requests",
"createDesc": "Allows parents/students to submit online leave requests",
"read": "Read Leave Requests",
"read.desc": "Allows viewing own/children/managed-class leave requests",
"readDesc": "Allows viewing own/children/managed-class leave requests",
"review": "Review Leave Requests",
"review.desc": "Allows homeroom teachers/admins to approve leave requests and auto-sync attendance"
"reviewDesc": "Allows homeroom teachers/admins to approve leave requests and auto-sync attendance"
},
"message": {
"send": "Send Message",
"send.desc": "Allows sending messages",
"sendDesc": "Allows sending messages",
"read": "Read Message",
"read.desc": "Allows viewing messages",
"readDesc": "Allows viewing messages",
"delete": "Delete Message",
"delete.desc": "Allows deleting messages"
"deleteDesc": "Allows deleting messages"
},
"scheduling": {
"auto": "Auto Scheduling",
"auto.desc": "Allows running automatic scheduling",
"autoDesc": "Allows running automatic scheduling",
"adjust": "Adjust Scheduling",
"adjust.desc": "Allows manually adjusting schedules"
"adjustDesc": "Allows manually adjusting schedules"
},
"elective": {
"manage": "Manage Electives",
"manage.desc": "Allows managing elective settings",
"manageDesc": "Allows managing elective settings",
"read": "Read Electives",
"read.desc": "Allows viewing elective information",
"readDesc": "Allows viewing elective information",
"select": "Select Elective",
"select.desc": "Allows students to select electives"
"selectDesc": "Allows students to select electives"
},
"diagnostic": {
"manage": "Manage Diagnostics",
"manage.desc": "Allows managing diagnostic tests",
"manageDesc": "Allows managing diagnostic tests",
"read": "Read Diagnostics",
"read.desc": "Allows viewing diagnostic results"
"readDesc": "Allows viewing diagnostic results"
},
"lesson_plan": {
"create": "Create Lesson Plan",
"create.desc": "Allows creating new lesson plans",
"createDesc": "Allows creating new lesson plans",
"read": "Read Lesson Plan",
"read.desc": "Allows viewing lesson plan list and details",
"readDesc": "Allows viewing lesson plan list and details",
"update": "Update Lesson Plan",
"update.desc": "Allows modifying lesson plan information",
"updateDesc": "Allows modifying lesson plan information",
"delete": "Delete Lesson Plan",
"delete.desc": "Allows deleting lesson plans",
"deleteDesc": "Allows deleting lesson plans",
"publish": "Publish Lesson Plan",
"publish.desc": "Allows publishing lesson plans"
"publishDesc": "Allows publishing lesson plans"
},
"dashboard": {
"admin_read": "Read Admin Dashboard",
"admin_read.desc": "Allows viewing admin dashboard",
"admin_readDesc": "Allows viewing admin dashboard",
"teacher_read": "Read Teacher Dashboard",
"teacher_read.desc": "Allows viewing teacher dashboard",
"teacher_readDesc": "Allows viewing teacher dashboard",
"student_read": "Read Student Dashboard",
"student_read.desc": "Allows viewing student dashboard",
"student_readDesc": "Allows viewing student dashboard",
"parent_read": "Read Parent Dashboard",
"parent_read.desc": "Allows viewing parent dashboard"
"parent_readDesc": "Allows viewing parent dashboard"
},
"error_book": {
"read": "Read Error Book",
"read.desc": "Allows viewing error book",
"readDesc": "Allows viewing error book",
"manage": "Manage Error Book",
"manage.desc": "Allows managing error records",
"manageDesc": "Allows managing error records",
"analytics_read": "Read Error Analytics",
"analytics_read.desc": "Allows viewing error book analytics"
"analytics_readDesc": "Allows viewing error book analytics"
},
"adaptive_practice": {
"read": "Read Adaptive Practice",
"read.desc": "Allows viewing adaptive practice",
"readDesc": "Allows viewing adaptive practice",
"manage": "Manage Adaptive Practice",
"manage.desc": "Allows managing adaptive practice"
"manageDesc": "Allows managing adaptive practice"
},
"rbac": {
"role_create": "Create Role",
"role_create.desc": "Allows creating new roles",
"role_createDesc": "Allows creating new roles",
"role_read": "Read Role",
"role_read.desc": "Allows viewing role list and details",
"role_readDesc": "Allows viewing role list and details",
"role_update": "Update Role",
"role_update.desc": "Allows modifying role information",
"role_updateDesc": "Allows modifying role information",
"role_delete": "Delete Role",
"role_delete.desc": "Allows deleting roles",
"role_deleteDesc": "Allows deleting roles",
"role_assign": "Assign Role",
"role_assign.desc": "Allows assigning roles to users",
"role_assignDesc": "Allows assigning roles to users",
"permission_read": "Read Permission",
"permission_read.desc": "Allows viewing permission catalog"
"permission_readDesc": "Allows viewing permission catalog"
}
},
"actions": {

View File

@@ -77,6 +77,12 @@
"description": "An unexpected error occurred. Please try again.",
"retry": "Try again"
},
"errors": {
"unexpected": "An unexpected error occurred"
},
"actions": {
"retry": "Try again"
},
"classDetail": {
"backToCourses": "Back to Courses",
"grade": "Grade {grade}",

View File

@@ -180,6 +180,12 @@
"loadFailedDesc": "An error occurred while loading the textbook content. Please try again.",
"retry": "Retry"
},
"errors": {
"unexpected": "An unexpected error occurred"
},
"actions": {
"retry": "Retry"
},
"action": {
"createSuccess": "Textbook created successfully.",
"createFailed": "Failed to create textbook.",
@@ -224,6 +230,10 @@
"noClassMasteryPermission": "No permission to view class mastery data"
},
"graph": {
"layout": {
"hierarchical": "Hierarchical",
"force": "Force Graph"
},
"viewMode": {
"structure": "Structure",
"studentMastery": "My Mastery",

View File

@@ -3,7 +3,13 @@
"student": "学生学情诊断",
"class": "班级学情诊断",
"reportList": "诊断报告",
"myDiagnostic": "我的学情诊断"
"myDiagnostic": "我的学情诊断",
"teacherReportList": "学情诊断",
"teacherReportListDesc": "查看并管理基于知识点掌握度的诊断报告。",
"teacherStudent": "学生诊断",
"teacherStudentDesc": "知识点掌握度分析与诊断报告。",
"teacherClass": "班级诊断",
"teacherClassDesc": "班级知识点掌握度概览与重点关注学生名单。"
},
"type": {
"individual": "个人",

View File

@@ -283,7 +283,7 @@
"reviewRecorded": "复习结果已记录",
"archived": "错题已归档",
"deleted": "错题已删除",
"collected": "已采集 {{count}} 道错题",
"collected": "已采集 {count} 道错题",
"noNewErrors": "没有新的错题需要采集",
"addFailed": "添加错题失败",
"saveFailed": "保存失败",

View File

@@ -70,7 +70,7 @@
"3": "中等",
"4": "偏难",
"5": "困难",
"ariaLabel": "难度等级 {{level}}{{label}}"
"ariaLabel": "难度等级 {level}{label}"
},
"actions": {
"preview": "预览考试",
@@ -83,7 +83,7 @@
"archive": "归档",
"delete": "删除",
"deleteConfirmTitle": "确定要删除吗?",
"deleteConfirmDescription": "此操作不可撤销。将永久删除考试\"{{title}}\"及所有关联数据。",
"deleteConfirmDescription": "此操作不可撤销。将永久删除考试\"{title}\"及所有关联数据。",
"cancel": "取消",
"deleteSuccess": "考试已删除",
"deleteFailed": "删除考试失败",
@@ -165,7 +165,7 @@
"richEditorHint": "切换到富文本编辑器",
"loaded": "已加载",
"startHint": "从右侧面板添加题目开始",
"itemsInStructure": "结构中有 {{count}} 项",
"itemsInStructure": "结构中有 {count} 项",
"saveSuccess": "考试草稿已保存",
"saveFailed": "保存失败",
"publishSuccess": "考试已发布",
@@ -280,7 +280,7 @@
"sectionLabel": "分卷",
"partLabel": "部分",
"groupLabel": "大题",
"questionCountSummary": "(共 {{count}} 题,{{score}} 分)",
"questionCountSummary": "(共 {count} 题,{score} 分)",
"typeSingleChoice": "单选",
"typeMultipleChoice": "多选",
"typeJudgment": "判断",
@@ -310,8 +310,8 @@
"taskInterrupted": "页面刷新后任务已中断,请重新生成",
"untitledExam": "未命名试卷",
"queuedSuccess": "已加入后台队列,可继续编辑页面",
"backgroundComplete": "后台生成完成:{{title}}",
"backgroundFailed": "后台生成失败:{{title}}",
"backgroundComplete": "后台生成完成:{title}",
"backgroundFailed": "后台生成失败:{title}",
"selectQuestionFirst": "请先选择一个题目",
"questionNotFound": "未找到选中的题目",
"enterRewriteInstruction": "请输入重写指令",
@@ -321,7 +321,7 @@
"pasteSourceFirst": "请先粘贴试卷文本"
},
"paperPreview": {
"scoreWithUnit": "({{score}}分)"
"scoreWithUnit": "({score}分)"
},
"actionMessages": {
"enterRewriteInstruction": "请输入重写指令",
@@ -361,16 +361,16 @@
"noQuestionText": "(无题目文本)"
},
"card": {
"level": "难度 {{level}}",
"minutes": "{{count}} 分钟",
"points": "{{count}} 分",
"questions": "{{count}} 题"
"level": "难度 {level}",
"minutes": "{count} 分钟",
"points": "{count} 分",
"questions": "{count} 题"
},
"viewer": {
"section": "分卷",
"group": "大题",
"score": "分值",
"scoreLabel": "分值:{{score}}",
"scoreLabel": "分值:{score}",
"noQuestions": "暂无题目",
"unknown": "未知"
},
@@ -379,7 +379,7 @@
"title": "试卷预览",
"generating": "生成预览中...",
"fullPreview": "完整试卷预览",
"summary": "{{count}} 题 · {{subject}} · {{grade}} · {{minutes}} 分钟 · {{total}} 分",
"summary": "{count} 题 · {subject} · {grade} · {minutes} 分钟 · {total} 分",
"noPreview": "暂无预览内容",
"confirmCreate": "确认并创建",
"untitledQuestion": "未命名题目",
@@ -390,7 +390,7 @@
"label": "选项",
"addOption": "新增选项",
"correct": "正确",
"markCorrectAria": "标记选项 {{id}} 为正确答案",
"markCorrectAria": "标记选项 {id} 为正确答案",
"deleteOptionAria": "删除选项"
},
"editorExtensions": {
@@ -400,7 +400,7 @@
"group": {
"titlePlaceholder": "大题标题(如:一、选择题)",
"instructionPlaceholder": "说明(如:每小题3分,共24分)—— 可留空,总分自动统计",
"statsSummary": "共 {{count}} 题 · {{score}} 分"
"statsSummary": "共 {count} 题 · {score} 分"
},
"question": {
"typeSingleChoice": "单选",
@@ -416,7 +416,7 @@
"levelVolume": "卷",
"levelPart": "部分",
"levelSubVolume": "分卷",
"statsSummary": "共 {{count}} 题 · {{score}} 分"
"statsSummary": "共 {count} 题 · {score} 分"
}
}
},
@@ -430,7 +430,7 @@
"emptyDescription": "您还没有创建任何作业。",
"emptyFilteredDescription": "尝试清除筛选条件或调整关键词。",
"clearFilters": "清除筛选",
"filterByClass": "按班级筛选:{{className}}",
"filterByClass": "按班级筛选:{className}",
"columns": {
"title": "标题",
"status": "状态",
@@ -488,7 +488,7 @@
},
"take": {
"questions": "题目",
"question": "第 {{index}} 题",
"question": "第 {index} 题",
"points": "分",
"startAssignment": "开始作答",
"submitAssignment": "提交作业",
@@ -505,12 +505,12 @@
"readyDescription": "点击上方\"开始作答\"按钮。点击\"保存答案\"将保存您的答案。",
"startNow": "立即开始",
"back": "返回",
"timedExam": "限时考试:{{minutes}} 分钟",
"timedExam": "限时考试:{minutes} 分钟",
"timeRemaining": "剩余时间",
"timeUpAutoSubmit": "时间到,正在自动提交...",
"confirmSubmit": "确认提交",
"confirmSubmitDescription": "所有题目已作答。提交后答案不可修改,确定要提交吗?",
"unansweredWarning": "您有 {{count}} 道题未作答。提交后答案不可修改,确定要提交吗?",
"unansweredWarning": "您有 {count} 道题未作答。提交后答案不可修改,确定要提交吗?",
"cancel": "取消",
"confirmSubmitAction": "确认提交",
"submitSuccess": "已提交",
@@ -521,15 +521,15 @@
"status": "状态",
"dueDate": "截止时间",
"overdue": "已逾期",
"hoursLeft": "还剩 {{hours}} 小时",
"hoursLeft": "还剩 {hours} 小时",
"lessThanOneHour": "不足 1 小时",
"attempts": "尝试次数",
"attemptsUsed": "已用 {{used}} / {{max}}",
"attemptsRemaining": "· 剩余 {{remaining}} 次",
"attemptsUsed": "已用 {used} / {max}",
"attemptsRemaining": "· 剩余 {remaining} 次",
"description": "描述",
"noDescription": "无描述。",
"progress": "进度",
"jumpToQuestion": "跳转到第 {{index}} 题",
"jumpToQuestion": "跳转到第 {index} 题",
"answered": "已作答",
"unanswered": "未作答",
"yourAnswer": "你的答案",
@@ -550,12 +550,12 @@
"scanDescription": "在纸上作答后,拍照上传整卷答题图片",
"selectImageFiles": "请选择图片文件",
"uploadFailed": "上传失败",
"uploadSuccess": "已上传 {{count}} 张图片",
"uploadSuccess": "已上传 {count} 张图片",
"deleteScan": "删除",
"moveUp": "上移",
"moveDown": "下移",
"dragDropHint": "拖拽图片到此处,或点击选择文件",
"pageLabel": "第 {{page}} 页",
"pageLabel": "第 {page} 页",
"scanDisabled": "已提交,无法修改答题图片"
},
"grade": {
@@ -589,7 +589,7 @@
"scoreLabel": "分数",
"addFeedback": "添加反馈",
"hideFeedback": "隐藏反馈",
"feedbackPlaceholder": "为 {{name}} 添加反馈...",
"feedbackPlaceholder": "为 {name} 添加反馈...",
"submitGrades": "提交成绩",
"saving": "保存中...",
"gradesSaved": "批改已保存",
@@ -600,7 +600,7 @@
"next": "下一页",
"gradesAutoSaveNote": "点击提交后成绩将自动保存。学生将在您提交后立即看到成绩和反馈。",
"batchAutoGrade": "批量自动批改",
"batchSelected": "已选 {{count}} 份提交",
"batchSelected": "已选 {count} 份提交",
"batchSelectAtLeastOne": "请至少选择一份提交",
"batchFailed": "批量批改失败",
"selectAll": "全选",
@@ -616,9 +616,9 @@
"noScans": "暂无答题图片",
"saveFailed": "保存失败",
"scanFeedbackPlaceholder": "批改评语(可选)...",
"scoreOutOf": "/ {{max}} 分",
"questionsCount": "题目与批改({{count}} 题)",
"scanPagesCount": "学生答题图片({{count}} 页)"
"scoreOutOf": "/ {max} 分",
"questionsCount": "题目与批改({count} 题)",
"scanPagesCount": "学生答题图片({count} 页)"
},
"review": {
"title": "复习",
@@ -700,15 +700,15 @@
"questionPreview": "题目预览",
"errorAnalysis": "错误分析",
"errorRateOverview": "错误率概览",
"errorRateAriaLabel": "错误率 {{rate}}%",
"errorRateAriaLabel": "错误率 {rate}%",
"question": "题目",
"errors": "错误数",
"errorRateLabel": "错误率",
"wrongAnswersWithCount": "错答 ({{count}})",
"wrongAnswersWithCount": "错答 ({count})",
"wrongAnswers": "错答",
"noWrongAnswers": "暂无错答记录。",
"studentAnswer": "学生答案",
"studentCount": "{{count}} 名学生",
"studentCount": "{count} 名学生",
"notAnswered": "未作答",
"selectQuestionHint": "请从左侧选择题目",
"selectQuestionHintDesc": "查看错误分析",
@@ -721,9 +721,9 @@
"zoomIn": "放大",
"rotate": "旋转",
"fullscreen": "全屏",
"pageIndicator": "第 {{current}} / {{total}} 页",
"answerImageAlt": "答题图 第{{page}}页",
"thumbnailAlt": "缩略图 {{page}}"
"pageIndicator": "第 {current} / {total} 页",
"answerImageAlt": "答题图 第{page}页",
"thumbnailAlt": "缩略图 {page}"
},
"submissions": {
"title": "作业提交",
@@ -744,26 +744,26 @@
},
"excellent": {
"title": "优秀作业展示",
"description": "本作业中得分率达到 {{minPercentage}}% 及以上的优秀样例。",
"description": "本作业中得分率达到 {minPercentage}% 及以上的优秀样例。",
"empty": "暂无符合条件的优秀作业。",
"emptyHint": "完成批改后将自动汇总展示。",
"loading": "加载优秀作业...",
"loadFailed": "加载失败",
"retry": "重试",
"rank": "第 {{rank}} 名",
"rank": "第 {rank} 名",
"scoreLabel": "得分",
"scoreValue": "{{score}} / {{max}}",
"percentage": "{{value}}%",
"scoreValue": "{score} / {max}",
"percentage": "{value}%",
"lateTag": "迟交",
"viewDetail": "查看详情",
"submittedAt": "提交于 {{date}}",
"submittedAt": "提交于 {date}",
"studentAnon": "同学"
},
"parentExam": {
"examsTaken": "已参加考试",
"averageScore": "平均分",
"bestScore": "最高分",
"examResults": "{{name}} 的考试成绩",
"examResults": "{name} 的考试成绩",
"examResultsDescription": "近期考试分数与表现趋势",
"noResults": "暂无考试成绩",
"noResultsHint": "考试成绩将在批改完成后显示。",

View File

@@ -1,12 +1,16 @@
{
"title": "请假申请",
"title.parent": "在线请假",
"title.teacher": "请假审批",
"title.student": "我的请假",
"description": "为您的孩子提交请假申请。",
"description.parent": "为子女提交请假申请,班主任审批后自动同步考勤。",
"description.teacher": "查看并审批本班学生提交的请假申请,审批通过后自动将请假期间考勤标记为「请假」。",
"description.student": "提交本人请假申请,班主任审批后生效。",
"title": {
"default": "请假申请",
"parent": "在线请假",
"teacher": "请假审批",
"student": "我的请假"
},
"description": {
"default": "为您的孩子提交请假申请。",
"parent": "为子女提交请假申请,班主任审批后自动同步考勤。",
"teacher": "查看并审批本班学生提交的请假申请,审批通过后自动将请假期间考勤标记为「请假」。",
"student": "提交本人请假申请,班主任审批后生效。"
},
"backToDashboard": "返回仪表盘",
"onlineLeave": "在线请假申请",
"comingSoon": "即将上线",

View File

@@ -27,7 +27,12 @@
"unpublishPlanConfirm": "撤回后学生和家长将无法查看此课案,确认撤回?",
"publishPlanSuccess": "课案已发布",
"unpublishPlanSuccess": "课案已撤回",
"viewHomework": "查看"
"viewHomework": "查看",
"undo": "撤销",
"undoShortcut": "撤销 (Ctrl+Z)",
"redo": "重做",
"redoShortcut": "重做 (Ctrl+Shift+Z)",
"scheduleLesson": "安排课时"
},
"status": {
"saving": "保存中...",
@@ -39,7 +44,15 @@
"published": "已发布",
"rejected": "已驳回",
"archived": "已归档",
"publishedAsHomework": "已发布为作业"
"publishedAsHomework": "已发布为作业",
"retrySave": "重试保存",
"saveFailed": "保存失败",
"saveFailedHint": "请检查网络后重试,编辑内容已保留",
"recovered": "已恢复保存",
"offline": "网络已断开",
"offlineHint": "编辑内容会保留在本地,恢复网络后自动保存",
"offlineBadge": "离线",
"backOnline": "网络已恢复"
},
"blockType": {
"objective": "教学目标",
@@ -130,7 +143,46 @@
"textbookLabel": "教材",
"chapterLabel": "课文",
"selectNodeForAnchor": "选择要关联的节点",
"createNewNode": "创建新节点并关联"
"createNewNode": "创建新节点并关联",
"autoLayout": "自动布局",
"autoLayoutHint": "按流程关系自动排列节点",
"stageLabel": "阶段",
"stageNone": "未归类",
"stage": {
"import": "导入",
"new_teaching": "新授",
"consolidation": "巩固",
"summary": "总结"
},
"differentiationLabel": "差异化",
"differentiationNone": "无",
"differentiation": {
"basic": "基础",
"intermediate": "提高",
"advanced": "拓展"
},
"consistencyTitle": "教学评一致性",
"consistencyScore": "一致性分数:{score}",
"consistencyCoverage": "目标覆盖:{covered}/{total}",
"consistencyOpen": "一致性校验",
"consistencyClose": "关闭",
"consistencyNoIssues": "未发现一致性问题",
"consistencyObjectiveCount": "目标 {count}",
"consistencyExerciseCount": "评价 {count}"
},
"consistency": {
"title": "教学评一致性校验",
"score": "一致性分数:{score}",
"coverage": "目标覆盖:{covered}/{total}",
"noIssues": "未发现一致性问题",
"code": {
"noObjective": "缺少教学目标节点(建议添加 objective 节点)",
"noExercise": "缺少评价节点(建议添加 exercise 节点)",
"objectiveNotAssessed": "目标「{title}」未被任何评价覆盖",
"exerciseNoKnowledgePoint": "评价「{title}」未关联任何知识点",
"exerciseWithoutObjective": "评价「{title}」未关联任何教学目标",
"objectiveNoKnowledgePoint": "目标「{title}」未标注知识点"
}
},
"readonly": {
"title": "查看课案",
@@ -160,6 +212,65 @@
"title": "年级备课",
"description": "查看本年级教师的备课"
},
"library": {
"title": "校内课案库",
"description": "浏览本校教师分享的优秀课案",
"empty": "暂无可参考的课案",
"fork": "复制为我的课案",
"forkSuccess": "已复制到我的课案",
"byCreator": "作者:{creator}",
"noCreator": "未知作者"
},
"feedback": {
"title": "AI 教学反馈",
"open": "AI 反馈",
"loading": "AI 正在分析课案...",
"loadFailed": "AI 反馈生成失败,请稍后重试",
"empty": "未生成反馈建议",
"summary": "整体评估",
"score": "评分",
"category": {
"strengths": "优点",
"improvements": "改进建议",
"alignment": "教学评一致性",
"differentiation": "差异化建议"
}
},
"aiDifferentiation": {
"title": "AI 差异化与课标核对",
"open": "AI 差异化",
"loading": "AI 正在分析...",
"loadFailed": "AI 分析失败,请稍后重试",
"empty": "未生成结果",
"noKnowledgePoints": "未提供知识点列表,无法进行课标核对",
"covered": "已覆盖 {count} 项",
"missed": "未覆盖 {count} 项",
"tabs": {
"differentiation": "差异化建议",
"curriculum": "课标核对",
"assessment": "可解释评估"
},
"level": {
"basic": "基础层",
"intermediate": "提高层",
"advanced": "拓展层"
}
},
"heatmap": {
"title": "课标覆盖热力图",
"description": "查看您所有课案对教材知识点的覆盖情况",
"totalKps": "知识点总数",
"coveredKps": "已覆盖",
"coverageRate": "覆盖率",
"blindSpotTitle": "{count} 个教学盲点",
"andMore": " 等",
"unknownChapter": "未知章节",
"chapterCoverage": "{covered}/{total}{rate}%",
"notCovered": "未覆盖",
"planCount": "{count} 个课案",
"selectTextbook": "请选择教材查看热力图",
"noData": "该教材暂无知识点数据"
},
"picker": {
"textbookLabel": "选择教材",
"chapterLabel": "选择课文",
@@ -172,7 +283,11 @@
"noChapters": "该教材暂无章节",
"selectedChapter": "已选课文:{chapter}",
"skeletonHint": "将自动生成以课文正文为核心的备课骨架(含 10 个教学节点)",
"errorTextbookChapterRequired": "请选择教材和课文"
"errorTextbookChapterRequired": "请选择教材和课文",
"searchTextbookPlaceholder": "搜索教材(标题/学科/年级)...",
"searchTextbookLabel": "搜索教材",
"recentSection": "最近使用",
"searchEmpty": "无匹配教材"
},
"filters": {
"searchPlaceholder": "搜索标题...",
@@ -203,6 +318,25 @@
"autoLabel": "自动版本",
"revertLabel": "回退到 v{versionNo}"
},
"diff": {
"summary": "新增 {added} · 删除 {removed} · 修改 {modified}",
"comparing": "对比 v{version} 与当前",
"noChanges": "两个版本内容一致,无差异",
"unchangedCount": "{count} 个节点未变更",
"added": "新增",
"removed": "删除",
"modified": "修改",
"unchanged": "未变",
"changedFields": "变更字段",
"compareWithCurrent": "对比当前",
"back": "返回",
"field": {
"title": "标题",
"stage": "阶段",
"differentiation": "差异化",
"data": "内容"
}
},
"knowledgePoint": {
"title": "选择知识点",
"empty": "未找到知识点,请先在教材模块创建",
@@ -232,6 +366,10 @@
"questionId": "题目 {id}",
"type": {
"single_choice": "单选题",
"multiple_choice": "多选题",
"true_false": "判断题",
"short_answer": "简答题",
"essay": "论述题",
"text": "填空题",
"judgment": "判断题"
},
@@ -246,7 +384,8 @@
"difficultyLabel": "难度",
"knowledgePointLabel": "知识点",
"stemRequired": "请输入题干",
"addBtn": "添加"
"addBtn": "添加",
"noDetail": "(无详细内容)"
},
"exercise": {
"purposeLabel": "用途",
@@ -271,7 +410,27 @@
"planNotFound": "课案不存在",
"noPermission": "无权发布",
"homeworkTitle": "{title} - 作业",
"homeworkDescription": "来自课案"
"homeworkDescription": "来自课案",
"step1": "选择班级",
"step2": "预览作业",
"step3": "确认发布",
"stepIndicator": "第 {current}/{total} 步:{label}",
"next": "下一步",
"back": "上一步",
"previewClassCount": "下发班级数",
"previewQuestionCount": "题目数量",
"previewTotalScore": "总分",
"previewQuestionList": "题目列表",
"previewSource": "来源:{source}",
"previewNoStem": "(无题干预览)",
"previewScore": "{score} 分",
"confirmTitle": "请确认发布信息",
"confirmClassCount": "下发班级数:{count}",
"confirmQuestionCount": "题目数量:{count}",
"confirmTotalScore": "总分:{score}",
"confirmAvailableAt": "开始时间:{time}",
"confirmDueAt": "截止时间:{time}",
"confirmWarning": "发布后学生和家长将立即收到作业,请仔细核对。"
},
"textStudy": {
"sourceTextLabel": "课文原文",
@@ -336,7 +495,12 @@
"text": "文字式"
},
"contentLabel": "板书内容",
"contentPlaceholder": "输入板书内容..."
"contentPlaceholder": "输入板书内容...",
"editMode": "编辑",
"previewMode": "预览",
"editHint": "提示:使用 2 个空格或 1 个 Tab 缩进表示层级",
"previewEmpty": "暂无内容,请切换到编辑模式输入",
"untitled": "中心主题"
},
"import": {
"hint": "设计课堂导入环节",
@@ -365,6 +529,45 @@
"richText": {
"placeholder": "输入内容..."
},
"export": {
"title": "导出/打印",
"button": "导出",
"print": "打印",
"variantDetailed": "详细版",
"variantConcise": "简洁版",
"teacher": "教师",
"class": "班级",
"totalDuration": "教学时长 {count} 分钟",
"lastSavedAt": "最后保存",
"empty": "课案无内容",
"emptySection": "(无内容)",
"footerHint": "教案导出 - {variant}"
},
"attachment": {
"title": "素材库",
"add": "上传文件",
"insert": "插入附件",
"libraryLabel": "本课案已上传附件",
"empty": "暂无附件,请先上传",
"delete": "删除",
"remove": "移除",
"embeddedCount": "已嵌入 {count} 个附件",
"createSuccess": "附件已添加",
"deleteSuccess": "附件已删除",
"uploadFailed": "上传失败",
"uploadSuccess": "上传成功",
"type": {
"reference": "参考资料",
"material": "教学素材",
"supplementary": "补充资源"
},
"kind": {
"image": "图片",
"audio": "音频",
"video": "视频",
"file": "文件"
}
},
"error": {
"getList": "获取课案列表失败",
"getOne": "获取课案失败",
@@ -394,7 +597,12 @@
"titleTooLong": "标题不能超过 255 个字符",
"templateRequired": "请选择模板",
"invalidDate": "日期格式无效",
"classRequired": "至少选择一个班级"
"classRequired": "至少选择一个班级",
"invalidInput": "输入数据无效",
"unauthorized": "未授权操作",
"planIdRequired": "课案 ID 必填",
"classIdRequired": "班级 ID 必填",
"dateRequired": "日期必填"
},
"confirm": {
"archive": "确认归档此课案?",
@@ -506,22 +714,6 @@
"updateSuccess": "已更新",
"deleteSuccess": "已删除"
},
"attachment": {
"title": "资源附件",
"add": "添加附件",
"empty": "暂无附件",
"type": {
"reference": "参考资料",
"material": "教学素材",
"supplementary": "补充材料"
},
"nameLabel": "名称",
"urlLabel": "链接",
"typeLabel": "类型",
"createSuccess": "附件已添加",
"deleteSuccess": "附件已删除",
"updateSuccess": "附件已更新"
},
"substitute": {
"title": "代课教师",
"add": "添加代课",
@@ -540,6 +732,24 @@
"deleteSuccess": "代课已删除",
"cancelConfirm": "确认取消此代课安排?"
},
"schedule": {
"title": "安排课时",
"boundList": "已绑定课时",
"empty": "暂无绑定课时",
"addNew": "添加新绑定",
"classLabel": "班级",
"dateLabel": "日期",
"periodLabel": "节次",
"durationLabel": "时长(分钟)",
"period": "第{n}节",
"duration": "{n}分钟",
"add": "添加",
"delete": "删除",
"selectClass": "请选择班级",
"selectDate": "请选择日期",
"addSuccess": "已添加课时绑定",
"deleteSuccess": "已删除课时绑定"
},
"evaluation": {
"title": "AI 课案评估",
"run": "开始评估",

View File

@@ -49,6 +49,8 @@
"error": {
"loadFailed": "通知加载失败",
"loadFailedDesc": "抱歉,加载通知时发生了意外错误。请稍后重试。",
"boundaryTitle": "通知区块加载失败",
"boundaryDescription": "加载通知数据时发生错误,请重试。",
"retry": "重试"
}
}

View File

@@ -87,211 +87,211 @@
},
"exam": {
"create": "创建考试",
"create.desc": "允许创建新考试",
"createDesc": "允许创建新考试",
"read": "查看考试",
"read.desc": "允许查看考试列表和详情",
"readDesc": "允许查看考试列表和详情",
"update": "更新考试",
"update.desc": "允许修改考试信息",
"updateDesc": "允许修改考试信息",
"delete": "删除考试",
"delete.desc": "允许删除考试",
"deleteDesc": "允许删除考试",
"duplicate": "复制考试",
"duplicate.desc": "允许复制现有考试",
"duplicateDesc": "允许复制现有考试",
"publish": "发布考试",
"publish.desc": "允许发布考试供学生作答",
"publishDesc": "允许发布考试供学生作答",
"ai_generate": "AI 生成考试",
"ai_generate.desc": "允许使用 AI 生成考试内容",
"ai_generateDesc": "允许使用 AI 生成考试内容",
"submit": "提交考试",
"submit.desc": "允许提交考试答卷",
"submitDesc": "允许提交考试答卷",
"proctor": "监考",
"proctor.desc": "允许执行监考操作",
"proctorDesc": "允许执行监考操作",
"proctor_read": "查看监考",
"proctor_read.desc": "允许查看监考信息"
"proctor_readDesc": "允许查看监考信息"
},
"homework": {
"create": "创建作业",
"create.desc": "允许创建新作业",
"createDesc": "允许创建新作业",
"grade": "批改作业",
"grade.desc": "允许批改学生作业",
"gradeDesc": "允许批改学生作业",
"submit": "提交作业",
"submit.desc": "允许提交作业"
"submitDesc": "允许提交作业"
},
"question": {
"create": "创建题目",
"create.desc": "允许创建新题目",
"createDesc": "允许创建新题目",
"read": "查看题目",
"read.desc": "允许查看题目列表和详情",
"readDesc": "允许查看题目列表和详情",
"update": "更新题目",
"update.desc": "允许修改题目信息",
"updateDesc": "允许修改题目信息",
"delete": "删除题目",
"delete.desc": "允许删除题目"
"deleteDesc": "允许删除题目"
},
"textbook": {
"create": "创建教材",
"create.desc": "允许创建新教材",
"createDesc": "允许创建新教材",
"read": "查看教材",
"read.desc": "允许查看教材列表和详情",
"readDesc": "允许查看教材列表和详情",
"update": "更新教材",
"update.desc": "允许修改教材信息",
"updateDesc": "允许修改教材信息",
"delete": "删除教材",
"delete.desc": "允许删除教材"
"deleteDesc": "允许删除教材"
},
"class": {
"create": "创建班级",
"create.desc": "允许创建新班级",
"createDesc": "允许创建新班级",
"read": "查看班级",
"read.desc": "允许查看班级列表和详情",
"readDesc": "允许查看班级列表和详情",
"update": "更新班级",
"update.desc": "允许修改班级信息",
"updateDesc": "允许修改班级信息",
"delete": "删除班级",
"delete.desc": "允许删除班级",
"deleteDesc": "允许删除班级",
"enroll": "学生入班",
"enroll.desc": "允许管理班级学生入班",
"enrollDesc": "允许管理班级学生入班",
"schedule": "班级排课",
"schedule.desc": "允许管理班级课程表"
"scheduleDesc": "允许管理班级课程表"
},
"school": {
"manage": "学校管理",
"manage.desc": "允许管理学校信息",
"manageDesc": "允许管理学校信息",
"grade_manage": "年级管理",
"grade_manage.desc": "允许管理年级信息",
"grade_manageDesc": "允许管理年级信息",
"user_manage": "用户管理",
"user_manage.desc": "允许管理系统用户"
"user_manageDesc": "允许管理系统用户"
},
"user": {
"profile_update": "更新个人资料",
"profile_update.desc": "允许更新用户个人资料"
"profile_updateDesc": "允许更新用户个人资料"
},
"ai": {
"chat": "AI 对话",
"chat.desc": "允许使用 AI 对话功能",
"chatDesc": "允许使用 AI 对话功能",
"configure": "AI 配置",
"configure.desc": "允许配置 AI 参数"
"configureDesc": "允许配置 AI 参数"
},
"settings": {
"admin": "系统设置",
"admin.desc": "允许管理系统设置"
"adminDesc": "允许管理系统设置"
},
"audit": {
"read": "查看审计日志",
"read.desc": "允许查看系统审计日志"
"readDesc": "允许查看系统审计日志"
},
"announcement": {
"manage": "管理公告",
"manage.desc": "允许创建、修改和删除公告",
"manageDesc": "允许创建、修改和删除公告",
"read": "查看公告",
"read.desc": "允许查看公告内容"
"readDesc": "允许查看公告内容"
},
"grade_record": {
"manage": "管理成绩",
"manage.desc": "允许录入和修改成绩",
"manageDesc": "允许录入和修改成绩",
"read": "查看成绩",
"read.desc": "允许查看成绩记录"
"readDesc": "允许查看成绩记录"
},
"file": {
"upload": "上传文件",
"upload.desc": "允许上传文件",
"uploadDesc": "允许上传文件",
"read": "查看文件",
"read.desc": "允许查看文件",
"readDesc": "允许查看文件",
"delete": "删除文件",
"delete.desc": "允许删除文件"
"deleteDesc": "允许删除文件"
},
"course_plan": {
"manage": "管理课程计划",
"manage.desc": "允许创建、修改和删除课程计划",
"manageDesc": "允许创建、修改和删除课程计划",
"read": "查看课程计划",
"read.desc": "允许查看课程计划"
"readDesc": "允许查看课程计划"
},
"attendance": {
"manage": "管理考勤",
"manage.desc": "允许录入和修改考勤记录",
"manageDesc": "允许录入和修改考勤记录",
"read": "查看考勤",
"read.desc": "允许查看考勤记录"
"readDesc": "允许查看考勤记录"
},
"leave_request": {
"create": "提交请假申请",
"create.desc": "允许家长/学生提交在线请假申请",
"createDesc": "允许家长/学生提交在线请假申请",
"read": "查看请假申请",
"read.desc": "允许查看本人/子女/所辖班级的请假申请",
"readDesc": "允许查看本人/子女/所辖班级的请假申请",
"review": "审批请假申请",
"review.desc": "允许班主任/管理员审批请假申请并自动同步考勤"
"reviewDesc": "允许班主任/管理员审批请假申请并自动同步考勤"
},
"message": {
"send": "发送消息",
"send.desc": "允许发送消息",
"sendDesc": "允许发送消息",
"read": "查看消息",
"read.desc": "允许查看消息",
"readDesc": "允许查看消息",
"delete": "删除消息",
"delete.desc": "允许删除消息"
"deleteDesc": "允许删除消息"
},
"scheduling": {
"auto": "自动排课",
"auto.desc": "允许执行自动排课",
"autoDesc": "允许执行自动排课",
"adjust": "调整排课",
"adjust.desc": "允许手动调整课程表"
"adjustDesc": "允许手动调整课程表"
},
"elective": {
"manage": "管理选课",
"manage.desc": "允许管理选课设置",
"manageDesc": "允许管理选课设置",
"read": "查看选课",
"read.desc": "允许查看选课信息",
"readDesc": "允许查看选课信息",
"select": "选课",
"select.desc": "允许学生进行选课"
"selectDesc": "允许学生进行选课"
},
"diagnostic": {
"manage": "管理诊断",
"manage.desc": "允许管理诊断测试",
"manageDesc": "允许管理诊断测试",
"read": "查看诊断",
"read.desc": "允许查看诊断结果"
"readDesc": "允许查看诊断结果"
},
"lesson_plan": {
"create": "创建教案",
"create.desc": "允许创建新教案",
"createDesc": "允许创建新教案",
"read": "查看教案",
"read.desc": "允许查看教案列表和详情",
"readDesc": "允许查看教案列表和详情",
"update": "更新教案",
"update.desc": "允许修改教案信息",
"updateDesc": "允许修改教案信息",
"delete": "删除教案",
"delete.desc": "允许删除教案",
"deleteDesc": "允许删除教案",
"publish": "发布教案",
"publish.desc": "允许发布教案"
"publishDesc": "允许发布教案"
},
"dashboard": {
"admin_read": "查看管理员仪表盘",
"admin_read.desc": "允许查看管理员仪表盘",
"admin_readDesc": "允许查看管理员仪表盘",
"teacher_read": "查看教师仪表盘",
"teacher_read.desc": "允许查看教师仪表盘",
"teacher_readDesc": "允许查看教师仪表盘",
"student_read": "查看学生仪表盘",
"student_read.desc": "允许查看学生仪表盘",
"student_readDesc": "允许查看学生仪表盘",
"parent_read": "查看家长仪表盘",
"parent_read.desc": "允许查看家长仪表盘"
"parent_readDesc": "允许查看家长仪表盘"
},
"error_book": {
"read": "查看错题本",
"read.desc": "允许查看错题本",
"readDesc": "允许查看错题本",
"manage": "管理错题本",
"manage.desc": "允许管理错题记录",
"manageDesc": "允许管理错题记录",
"analytics_read": "查看错题分析",
"analytics_read.desc": "允许查看错题本分析数据"
"analytics_readDesc": "允许查看错题本分析数据"
},
"adaptive_practice": {
"read": "查看专项练习",
"read.desc": "允许查看专项练习",
"readDesc": "允许查看专项练习",
"manage": "管理专项练习",
"manage.desc": "允许管理专项练习"
"manageDesc": "允许管理专项练习"
},
"rbac": {
"role_create": "创建角色",
"role_create.desc": "允许创建新角色",
"role_createDesc": "允许创建新角色",
"role_read": "查看角色",
"role_read.desc": "允许查看角色列表和详情",
"role_readDesc": "允许查看角色列表和详情",
"role_update": "更新角色",
"role_update.desc": "允许修改角色信息",
"role_updateDesc": "允许修改角色信息",
"role_delete": "删除角色",
"role_delete.desc": "允许删除角色",
"role_deleteDesc": "允许删除角色",
"role_assign": "分配角色",
"role_assign.desc": "允许为用户分配角色",
"role_assignDesc": "允许为用户分配角色",
"permission_read": "查看权限",
"permission_read.desc": "允许查看权限目录"
"permission_readDesc": "允许查看权限目录"
}
},
"actions": {

View File

@@ -77,6 +77,12 @@
"description": "发生了意外错误,请稍后重试。",
"retry": "重试"
},
"errors": {
"unexpected": "发生未知错误"
},
"actions": {
"retry": "重试"
},
"classDetail": {
"backToCourses": "返回课程",
"grade": "年级 {grade}",

View File

@@ -180,6 +180,12 @@
"loadFailedDesc": "加载教材内容时发生错误,请重试。",
"retry": "重试"
},
"errors": {
"unexpected": "发生未知错误"
},
"actions": {
"retry": "重试"
},
"action": {
"createSuccess": "教材创建成功。",
"createFailed": "创建教材失败。",
@@ -224,6 +230,10 @@
"noClassMasteryPermission": "无权查看班级掌握度数据"
},
"graph": {
"layout": {
"hierarchical": "分层布局",
"force": "力导向图"
},
"viewMode": {
"structure": "结构图",
"studentMastery": "个人掌握度",

View File

@@ -19,14 +19,14 @@ export type { RateLimiter, RateLimitParams, RateLimitResult } from "./types"
export { RATE_LIMIT_RULES, rateLimitHeaders, rateLimitKey } from "./rules"
import { MemoryRateLimiter } from "./memory-limiter"
import { RedisRateLimiter } from "./redis-limiter"
/**
* 单例限流器实例(按 env 选择实现,进程级复用)。
*
* RedisRateLimiter 类本身在 import 时不会加载 @upstash/* 依赖——
* 重依赖通过类方法内的 `await import(...)` 懒加载,
* 仅当 `RATE_LIMIT_DRIVER=redis` 且实际调用 `limit()` 时才加载。
* Redis 实现通过动态 `import("./redis-limiter")` 按需加载,
* 仅当 `RATE_LIMIT_DRIVER=redis` 时才会拉入 `redis-limiter.ts` 模块及其
* `@upstash/ratelimit` / `@upstash/redis` 依赖。默认内存模式下该模块不进入
* 模块图,避免可选依赖缺失导致编译失败。
*/
let singleton: RateLimiter | null = null
@@ -34,13 +34,16 @@ let singleton: RateLimiter | null = null
* 获取当前进程的限流器实例。
*
* - 默认返回内存实现
* - 当 `RATE_LIMIT_DRIVER=redis` 时返回 Redis 实现
* - Redis 实现懒加载所需依赖,未安装 @upstash/ratelimit/redis 时首次调用抛错
* - 当 `RATE_LIMIT_DRIVER=redis` 时动态加载 Redis 实现
* - Redis 实现懒加载 @upstash/* 依赖,未安装时首次调用抛错
*
* 返回 `Promise<RateLimiter>`Redis 实现需动态 import 模块,故为异步。
*/
export function getRateLimiter(): RateLimiter {
export async function getRateLimiter(): Promise<RateLimiter> {
if (singleton) return singleton
if (env.RATE_LIMIT_DRIVER === "redis") {
const { RedisRateLimiter } = await import("./redis-limiter")
singleton = new RedisRateLimiter()
} else {
singleton = new MemoryRateLimiter()
@@ -56,7 +59,7 @@ export function getRateLimiter(): RateLimiter {
* **变更**:返回 `Promise<RateLimitResult>`,调用方需 `await`。
*/
export function rateLimit(params: RateLimitParams): Promise<RateLimitResult> {
return getRateLimiter().limit(params)
return getRateLimiter().then((limiter) => limiter.limit(params))
}
/**
@@ -67,5 +70,5 @@ export function rateLimit(params: RateLimitParams): Promise<RateLimitResult> {
* **变更**:返回 `Promise<void>`,调用方需 `await`。
*/
export function resetRateLimit(key: string): Promise<void> {
return getRateLimiter().reset(key)
return getRateLimiter().then((limiter) => limiter.reset(key))
}

View File

@@ -94,7 +94,8 @@ export class RedisRateLimiter implements RateLimiter {
/** 懒加载 @upstash/redis 的 Redis 客户端 */
private async getRedisClient(): Promise<unknown> {
if (this.redisClient) return this.redisClient
const { Redis } = await import("@upstash/redis")
// webpackIgnore: 让 @upstash/redis 成为运行时可选依赖,未启用 redis 驱动时不要求安装
const { Redis } = await import(/* webpackIgnore: true */ "@upstash/redis")
this.redisClient = new Redis({
url: env.UPSTASH_REDIS_REST_URL!,
token: env.UPSTASH_REDIS_REST_TOKEN!,
@@ -106,7 +107,8 @@ export class RedisRateLimiter implements RateLimiter {
private async getRatelimitCtor(): Promise<UpstashRatelimitCtor> {
if (this.ratelimitCtorPromise) return this.ratelimitCtorPromise
this.ratelimitCtorPromise = (async () => {
const mod = await import("@upstash/ratelimit")
// webpackIgnore: 让 @upstash/ratelimit 成为运行时可选依赖,未启用 redis 驱动时不要求安装
const mod = await import(/* webpackIgnore: true */ "@upstash/ratelimit")
return mod.Ratelimit as unknown as UpstashRatelimitCtor
})()
return this.ratelimitCtorPromise