feat(lesson-preparation): 师生交互对话体 inline 渲染
This commit is contained in:
@@ -0,0 +1,91 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
import type { InteractionBlockData, LessonPlanNode, QATurn } from "../../types";
|
||||||
|
import { useLessonPlanEditor } from "../../hooks/use-lesson-plan-editor";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
node: LessonPlanNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* V4 师生交互节点的 inline 对话体渲染。
|
||||||
|
* - 师/生 角色标签(JetBrains Mono)
|
||||||
|
* - 教师提问下方 italic 灰色 [预期:...]
|
||||||
|
* - 对话轮次之间 dashed 线分隔
|
||||||
|
*/
|
||||||
|
export function InlineQaDialog({ node }: Props) {
|
||||||
|
const t = useTranslations("lessonPreparation");
|
||||||
|
const toggleExpand = useLessonPlanEditor((s) => s.toggleExpand);
|
||||||
|
const data = node.data as InteractionBlockData;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="lp-inline-node">
|
||||||
|
<div className="lp-inline-node-head">
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
width: 5,
|
||||||
|
height: 5,
|
||||||
|
borderRadius: "50%",
|
||||||
|
background: "var(--lp-dot-interaction)",
|
||||||
|
display: "inline-block",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span>{t("v4.interaction.label")}</span>
|
||||||
|
<span style={{ marginLeft: "auto", fontFamily: "JetBrains Mono, monospace", fontSize: 9, color: "var(--lp-inline-node-meta)" }}>
|
||||||
|
{data.turns?.length ?? 0} 轮
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => toggleExpand(node.id)}
|
||||||
|
style={{
|
||||||
|
cursor: "pointer",
|
||||||
|
padding: "2px 6px",
|
||||||
|
borderRadius: 3,
|
||||||
|
color: "var(--lp-inline-node-meta)",
|
||||||
|
fontSize: 11,
|
||||||
|
background: "transparent",
|
||||||
|
border: "none",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("v4.contextMenu.collapseFromPaper")} ▴
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<h4 className="lp-inline-node-title">{node.title}</h4>
|
||||||
|
<div className="lp-inline-node-body">
|
||||||
|
{data.designIntent && (
|
||||||
|
<p style={{ fontSize: 13, marginBottom: 8 }}>{data.designIntent}</p>
|
||||||
|
)}
|
||||||
|
<div className="lp-qa-dialog">
|
||||||
|
{(data.turns ?? []).map((turn, idx) => (
|
||||||
|
<QaTurnView key={turn.id} turn={turn} index={idx} />
|
||||||
|
))}
|
||||||
|
{(!data.turns || data.turns.length === 0) && (
|
||||||
|
<p style={{ color: "var(--muted-foreground)", fontSize: 12 }}>
|
||||||
|
—
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function QaTurnView({ turn, index }: { turn: QATurn; index: number }) {
|
||||||
|
const t = useTranslations("lessonPreparation");
|
||||||
|
const roleClass = turn.role === "teacher" ? "teacher" : "student";
|
||||||
|
const roleLabel = turn.role === "teacher" ? t("v4.interaction.roleTeacher") : t("v4.interaction.roleStudent");
|
||||||
|
void index;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="lp-qa-turn">
|
||||||
|
<div className={`lp-qa-role ${roleClass}`}>{roleLabel}</div>
|
||||||
|
<div className="lp-qa-content">
|
||||||
|
{turn.content}
|
||||||
|
{turn.role === "teacher" && turn.expectedAnswer && (
|
||||||
|
<span className="lp-qa-prompt">[预期:{turn.expectedAnswer}]</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user