feat(exams): add section/group structure nodes with auto stats
Distinguish structural levels from questions:
- sectionBlock (new): top-level volume like "第Ⅰ卷 选择题(共24分)"
- groupBlock (enhanced): section like "一、选择题" with instruction field
- questionBlock (composite): reserved for reading comprehension
Key changes:
- New section-block.tsx extension with level attr (1=卷, 2=部分) and
auto-computed question count + total score in NodeView
- group-block.tsx: add instruction field ("每小题3分"), auto stats display
- editor-to-structure.ts: recursive buildStructureNode supports arbitrary
nesting (section > group > question), computeStats accumulates scores
- exam-rich-form.tsx ExamPreview: render section/group/question with
distinct styles and stats badges
- selection-toolbar.tsx: add "分卷" button (Layers icon)
- exam-rich-editor.tsx: register SectionBlock, expose insertSection/
wrapInSection via ref
- actions.ts: AI prompt now outputs volumes[] + groups[] structure with
instruction; buildTiptapDocFromAiResponse generates nested sectionBlock
- i18n: add markSection keys (zh-CN/en)
Structural nodes are NOT questions: their question count and total score
are automatically computed from child questions, not manually set.
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
CircleSlash,
|
||||
FileText,
|
||||
Heading,
|
||||
Layers,
|
||||
Underline,
|
||||
} from "lucide-react"
|
||||
|
||||
@@ -137,9 +138,18 @@ export function SelectionToolbar({
|
||||
const insertGroup = () => {
|
||||
const chain = editor.chain().focus()
|
||||
if (hasTextSelection) {
|
||||
chain.wrapInGroup("一、选择题").run()
|
||||
chain.wrapInGroup("一、选择题", "").run()
|
||||
} else {
|
||||
chain.insertGroup("一、选择题").run()
|
||||
chain.insertGroup("一、选择题", "").run()
|
||||
}
|
||||
}
|
||||
|
||||
const insertSection = () => {
|
||||
const chain = editor.chain().focus()
|
||||
if (hasTextSelection) {
|
||||
chain.wrapInSection("第Ⅰ卷 选择题", 1).run()
|
||||
} else {
|
||||
chain.insertSection("第Ⅰ卷 选择题", 1).run()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,6 +199,7 @@ export function SelectionToolbar({
|
||||
transform: "translateX(-50%)",
|
||||
}}
|
||||
>
|
||||
<ToolbarButton onClick={insertSection} icon={Layers} label="分卷" />
|
||||
<ToolbarButton onClick={insertGroup} icon={Heading} label="大题" />
|
||||
<ToolbarButton
|
||||
onClick={() => insertQuestion("single_choice")}
|
||||
|
||||
Reference in New Issue
Block a user