feat(lesson-preparation): 右栏详情面板容器
This commit is contained in:
@@ -0,0 +1,111 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
import { useLessonPlanEditor } from "../../hooks/use-lesson-plan-editor";
|
||||||
|
import { DetailHead } from "./detail-head";
|
||||||
|
import { DetailProps } from "./detail-props";
|
||||||
|
import { QaEditor } from "./qa-editor";
|
||||||
|
import { BlockRenderer } from "../../config/block-registry";
|
||||||
|
import type { InteractionBlockData, LessonPlanNode } from "../../types";
|
||||||
|
|
||||||
|
export function DetailPanel() {
|
||||||
|
const t = useTranslations("lessonPreparation");
|
||||||
|
const doc = useLessonPlanEditor((s) => s.doc);
|
||||||
|
const selectedNodeId = useLessonPlanEditor((s) => s.selectedNodeId);
|
||||||
|
const expandedNodeIds = useLessonPlanEditor((s) => s.expandedNodeIds);
|
||||||
|
const updateNode = useLessonPlanEditor((s) => s.updateNode);
|
||||||
|
|
||||||
|
const node = doc.nodes.find(
|
||||||
|
(n): n is LessonPlanNode => n.id === selectedNodeId && n.type !== "textbook_content",
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!node) {
|
||||||
|
return (
|
||||||
|
<aside
|
||||||
|
style={{
|
||||||
|
borderLeft: "1px solid var(--border)",
|
||||||
|
background: "var(--background)",
|
||||||
|
padding: 24,
|
||||||
|
color: "var(--muted-foreground)",
|
||||||
|
fontSize: 13,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
选择左侧节点查看详情
|
||||||
|
</aside>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const isExpanded = expandedNodeIds.includes(node.id);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<aside
|
||||||
|
style={{
|
||||||
|
borderLeft: "1px solid var(--border)",
|
||||||
|
background: "var(--background)",
|
||||||
|
overflowY: "auto",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DetailHead node={node} isExpanded={isExpanded} />
|
||||||
|
<DetailProps node={node} />
|
||||||
|
|
||||||
|
<div style={{ flex: 1, padding: 20, overflowY: "auto" }}>
|
||||||
|
{node.type === "interaction" ? (
|
||||||
|
<QaEditor nodeId={node.id} data={node.data as InteractionBlockData} />
|
||||||
|
) : (
|
||||||
|
<BlockRenderer
|
||||||
|
type={node.type}
|
||||||
|
blockId={node.id}
|
||||||
|
data={node.data}
|
||||||
|
onUpdate={(data) => updateNode(node.id, { data })}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
borderTop: "1px solid var(--border)",
|
||||||
|
padding: "14px 20px",
|
||||||
|
background: "var(--muted)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: 11,
|
||||||
|
fontWeight: 600,
|
||||||
|
color: "var(--lp-interaction)",
|
||||||
|
marginBottom: 8,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
✦ {t("v4.detail.aiAssist")}
|
||||||
|
</div>
|
||||||
|
<div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
|
||||||
|
<AiButton label={t("v4.detail.aiGenerateLayered")} />
|
||||||
|
<AiButton label={t("v4.detail.aiFillExpected")} />
|
||||||
|
<AiButton label={t("v4.detail.aiOptimizeFollowup")} />
|
||||||
|
<AiButton label={t("v4.detail.aiDifferentiation")} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function AiButton({ label }: { label: string }) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
style={{
|
||||||
|
padding: "5px 10px",
|
||||||
|
border: "1px solid var(--border)",
|
||||||
|
background: "var(--background)",
|
||||||
|
borderRadius: 4,
|
||||||
|
fontSize: 11.5,
|
||||||
|
cursor: "pointer",
|
||||||
|
color: "var(--foreground)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user