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