|
| 1 | +/** |
| 2 | + * ClassroomSourceColumn — 课堂助手左栏配置列(批 0-C2 Task 4 步 4 自 |
| 3 | + * ClassroomPage.tsx 抽出,纯搬运、行为等价)。 |
| 4 | + * |
| 5 | + * @ai-context: 结构 = 折叠态单元素(ColumnBar 窄条)或展开态外壳 div(标题「📡 课堂助手」+ |
| 6 | + * 健康徽标 + 折叠钮 → 横幅插槽 → 滚动容器:就绪清单 / 系统窗口开关 / |
| 7 | + * 窗口选择卡 / 捕获卡插槽 / 四条采集动线面板 / 状态行)。横幅与捕获卡由 |
| 8 | + * 页面以 ReactNode 注入(banners / capturePanel)——本文件不订阅任何事件、 |
| 9 | + * 不持有状态,全部输入经 props。 |
| 10 | + * @ai-context: ★ 顶层恰好返回**一个**元素(折叠态 ColumnBar,否则外壳 div)——根 flex |
| 11 | + * 只有三个直接子元素(本列 + ColumnResizer + 右栏),多包一层或返回 |
| 12 | + * Fragment 会改变 flex 分配与 borderRight/宽度语义,**且无编译错误**。 |
| 13 | + * 同理 `<ColumnResizer>` 是本列的**兄弟**、由页面在折叠三元之外无条件渲染, |
| 14 | + * 不得搬进本文件(否则折叠态拖拽手柄消失)。 |
| 15 | + * @ai-context: 耦合逐字保留 —— 外壳内联样式键(width / flexShrink / borderRight / |
| 16 | + * display / flexDirection / minWidth)、`title="折叠侧栏"`、折叠钮 `⟨`、 |
| 17 | + * 文案「显示系统窗口(终端/资源管理器等)」、色值 #e5e7eb/#9ca3af/#6b7280、 |
| 18 | + * 间距 padding 10px 14px / gap 12 / padding 12 均为现状契约;本文件零 |
| 19 | + * className / 零 id / 零 data-testid,不 token 化、不换 emoji。 |
| 20 | + * @ai-context: 边界 —— 就绪清单(ReadyCheckCard)与页内模型卡的矛盾(G6)、采集中仍可 |
| 21 | + * 改选窗口(G7)是既有缺陷,本批只搬不改。 |
| 22 | + */ |
| 23 | +import { WindowSelectCard } from "./WindowSelectCard"; |
| 24 | +import VideoImportPanel from "./VideoImportPanel"; |
| 25 | +import ReadyCheckCard from "./ReadyCheckCard"; |
| 26 | +import { SystemStatusBadge } from "./SystemStatusBadge"; |
| 27 | +import ColumnBar from "./ColumnBar"; |
| 28 | +import MaterialInputPanel from "./MaterialInputPanel"; |
| 29 | +import PhotoCapturePanel from "./PhotoCapturePanel"; |
| 30 | +import WebImportPanel from "./WebImportPanel"; |
| 31 | +import type { ReactNode } from "react"; |
| 32 | +import type { Note, WindowInfo } from "../types"; |
| 33 | + |
| 34 | +interface Props { |
| 35 | + /** 生效折叠态(useColumnLayout.folded:自动折叠或手动折叠任一成立) */ |
| 36 | + folded: boolean; |
| 37 | + /** 生效列宽(useColumnLayout.width,已夹取 min..max) */ |
| 38 | + width: number; |
| 39 | + /** 点窄条展开(useColumnLayout.expand:同时清自动/手动折叠态) */ |
| 40 | + onExpand: () => void; |
| 41 | + /** 标题栏折叠钮(useColumnLayout.setManualFolded(true)) */ |
| 42 | + onFold: () => void; |
| 43 | + /** 页面级提示横幅插槽(ClassroomBanners——须保持为左栏 div 的直接子元素) */ |
| 44 | + banners: ReactNode; |
| 45 | + /** 实时捕获卡插槽(ClassroomCapturePanel) */ |
| 46 | + capturePanel: ReactNode; |
| 47 | + /** 窗口枚举结果(系统窗口开关的可显性判据 + 列表数据源) */ |
| 48 | + windows: WindowInfo[]; |
| 49 | + selectedWindow: WindowInfo | null; |
| 50 | + onSelectWindow: (win: WindowInfo) => void; |
| 51 | + /** 重新枚举窗口(background=true 时不显示 loading——列表内刷新用) */ |
| 52 | + onRefreshWindows: (background?: boolean) => void; |
| 53 | + windowsLoading: boolean; |
| 54 | + /** 显示系统窗口(终端/资源管理器等)开关 */ |
| 55 | + showSystemWindows: boolean; |
| 56 | + onShowSystemWindows: (show: boolean) => void; |
| 57 | + onOpenSessions?: (sessionId: number) => void; |
| 58 | + /** MaterialInputPanel 产物回填(页面 lastNote) */ |
| 59 | + onNote: (note: Note) => void; |
| 60 | + /** 状态行(页面 status 单一状态源) */ |
| 61 | + status: string; |
| 62 | + onStatus: (message: string) => void; |
| 63 | +} |
| 64 | + |
| 65 | +export default function ClassroomSourceColumn({ |
| 66 | + folded, |
| 67 | + width, |
| 68 | + onExpand, |
| 69 | + onFold, |
| 70 | + banners, |
| 71 | + capturePanel, |
| 72 | + windows, |
| 73 | + selectedWindow, |
| 74 | + onSelectWindow, |
| 75 | + onRefreshWindows, |
| 76 | + windowsLoading, |
| 77 | + showSystemWindows, |
| 78 | + onShowSystemWindows, |
| 79 | + onOpenSessions, |
| 80 | + onNote, |
| 81 | + status, |
| 82 | + onStatus, |
| 83 | +}: Props) { |
| 84 | + return folded ? ( |
| 85 | + <ColumnBar icon="📡" title="课堂助手" onClick={onExpand} /> |
| 86 | + ) : ( |
| 87 | + <div |
| 88 | + style={{ |
| 89 | + width, |
| 90 | + flexShrink: 0, |
| 91 | + borderRight: "1px solid #e5e7eb", |
| 92 | + display: "flex", |
| 93 | + flexDirection: "column", |
| 94 | + minWidth: 0, |
| 95 | + }} |
| 96 | + > |
| 97 | + <div style={{ padding: "10px 14px", borderBottom: "1px solid #e5e7eb", fontWeight: 600, display: "flex", alignItems: "center", justifyContent: "space-between" }}> |
| 98 | + <span>📡 课堂助手</span> |
| 99 | + <div style={{ display: "flex", alignItems: "center", gap: 4 }}> |
| 100 | + {/* M7/REQ-042 F2/G2:健康徽标 + 诊断面板(开发期可见) */} |
| 101 | + <SystemStatusBadge /> |
| 102 | + <button onClick={() => onFold()} style={{ fontSize: 12, cursor: "pointer", border: "none", background: "none", color: "#9ca3af" }} title="折叠侧栏">⟨</button> |
| 103 | + </div> |
| 104 | + </div> |
| 105 | + |
| 106 | + {banners} |
| 107 | + |
| 108 | + <div style={{ flex: 1, minHeight: 0, padding: 12, overflowY: "auto", display: "flex", flexDirection: "column", gap: 12 }}> |
| 109 | + {/* 2026-08 C1:引擎与模型就绪清单(开始前准备流——缺什么一目了然) */} |
| 110 | + <ReadyCheckCard /> |
| 111 | + |
| 112 | + {/* 目标窗口/进程选择(v0.2.0 实时捕获上下文) */} |
| 113 | + {/* v0.19.2(用户实测):系统窗口默认过滤,开关找回(能力不丢) */} |
| 114 | + {windows.some((w) => w.systemWindow) && ( |
| 115 | + <label style={{ fontSize: 11, color: "#6b7280", display: "flex", alignItems: "center", gap: 4, cursor: "pointer" }}> |
| 116 | + <input |
| 117 | + type="checkbox" |
| 118 | + checked={showSystemWindows} |
| 119 | + onChange={(e) => onShowSystemWindows(e.target.checked)} |
| 120 | + /> |
| 121 | + 显示系统窗口(终端/资源管理器等) |
| 122 | + </label> |
| 123 | + )} |
| 124 | + <WindowSelectCard |
| 125 | + windows={showSystemWindows ? windows : windows.filter((w) => !w.systemWindow)} |
| 126 | + selected={selectedWindow} |
| 127 | + onSelect={onSelectWindow} |
| 128 | + onRefresh={onRefreshWindows} |
| 129 | + loading={windowsLoading} |
| 130 | + /> |
| 131 | + |
| 132 | + {capturePanel} |
| 133 | + |
| 134 | + {/* 视频文件导入(v0.3.0:REQ-015 第二入口,字幕优先 + ASR fallback) */} |
| 135 | + <VideoImportPanel onOpenSessions={onOpenSessions} /> |
| 136 | + |
| 137 | + {/* 2026-08-21 用户需求:OCR 设备/音频预处理/备份/AI 服务/词表/模型管理等 |
| 138 | + 设置类面板已迁出至「⚙ 设置」页——课堂助手左栏仅保留采集动线 */} |
| 139 | + |
| 140 | + {/* 素材输入 + 提取按钮(v0.1.0 文件流水线;审查硬拆——MaterialInputPanel) */} |
| 141 | + <MaterialInputPanel |
| 142 | + windowTitle={selectedWindow?.title ?? null} |
| 143 | + onNote={onNote} |
| 144 | + onStatus={onStatus} |
| 145 | + /> |
| 146 | + |
| 147 | + {/* v0.11.7:图文采集(第三动线:截屏导入图文内容 → 图文会话) */} |
| 148 | + <PhotoCapturePanel onOpenSessions={onOpenSessions} onStatus={onStatus} /> |
| 149 | + |
| 150 | + {/* v0.20.4(REQ-303):web 采集(第四条动线:URL 静态直取 → kind=web 会话) */} |
| 151 | + <WebImportPanel onOpenSessions={onOpenSessions} onStatus={onStatus} /> |
| 152 | + |
| 153 | + {status && <p style={{ fontSize: 12, color: "#2563eb" }}>{status}</p>} |
| 154 | + </div> |
| 155 | + </div> |
| 156 | + ); |
| 157 | +} |
0 commit comments