import { For, Show, createEffect, type JSX } from "solid-js"; import type { CourseKind, RecordType } from "~/service/wk"; import type { AccountItem } from "~/store/account"; import type { CourseType } from "~/types/Course"; import type { RecordItem } from "~/service/wk"; type RecordTypeOption = { label: string; value: RecordType; }; type CourseRecordTypeOption = { label: string; value: CourseKind; }; interface CourseWorkspaceProps { selectedAccount: AccountItem | null; selectedCourseList: CourseType[]; selectedCourseId: number | null; selectedCourse: CourseType | null; recordType: RecordType; courseKind: CourseKind; currentCourseKindLabel: string; showingCachedRecords: boolean; recordTypeOptions: RecordTypeOption[]; courseRecordTypeOptions: CourseRecordTypeOption[]; records: RecordItem[]; studyLogs: string[]; recordsLoading: boolean; recordError: string; isRefreshingRecords: boolean; isRefreshingCourseRecords: boolean; isRunningStudy: boolean; isRefreshingLogs: boolean; autoScrollLogs: boolean; showLogTimestamps: boolean; densityMode: "comfortable" | "compact"; logFontSize: number; onSelectCourse: (courseId: number) => void; onRefreshRecords: () => void; onRefreshCourseRecords: () => void; onChangeRecordType: (value: RecordType) => void; onChangeCourseRecordType: (value: CourseKind) => void; onStartStudy: () => void; onStopStudy: () => void; onRefreshLogs: () => void; onClearLogs: () => void; renderRecordState: (value: string) => string; } const EmptyState = (props: { children: JSX.Element }) => (
{props.children}
); const extractTimestamp = (message: string) => { const match = message.match(/^\[(\d{2}:\d{2}:\d{2})\]\s*/); return match?.[1] ?? null; }; const stripTimestamp = (message: string) => { return message.replace(/^\[(\d{2}:\d{2}:\d{2})\]\s*/, ""); }; const CourseWorkspace = (props: CourseWorkspaceProps) => { let logContainerRef: HTMLDivElement | undefined; createEffect(() => { props.studyLogs.length; if (!props.autoScrollLogs) { return; } requestAnimationFrame(() => { const element = logContainerRef; if (element) { element.scrollTo({ top: element.scrollHeight, behavior: "smooth" }); } }); }); createEffect(() => { if (logContainerRef) { logContainerRef.style.fontSize = `${props.logFontSize}px`; } }); const compact = () => props.densityMode === "compact"; return (

课程工作台

课程、记录与日志统一查看

{props.selectedAccount?.user.name}
选择左侧账号后,这里会显示课程、记录和运行日志。 } >

课程列表

点击课程查看对应记录

当前“{props.currentCourseKindLabel} ”下没有课程,请切换分类或刷新课程列表。 {(course) => { const selected = () => course.id === props.selectedCourseId; return ( ); }}

记录列表

{props.selectedCourse ? props.selectedCourse.name : "请选择课程"}

当前显示的是本地缓存记录。

正在加载记录...
{props.recordError}
点击左侧课程后,在这里查看对应记录。 当前分类下没有记录。
{(record) => (

{record.name}

记录 ID:{record.id} | 章节:{record.chapterId}

{props.renderRecordState(record.state) || "未知状态"}

视频时长:{record.videoDuration}

学习秒数:{record.duration}

学习进度:{record.progress}

开始时间:{record.beginTime || "-"}

完成时间:{record.finalTime || "-"}

查看次数:{record.viewCount}

)}

运行日志

输出会自动滚动到最新位置

0} fallback={

暂无日志输出。

} >
{(log, index) => (

[{index() + 1}]{" "} {props.showLogTimestamps && extractTimestamp(log) ? `${extractTimestamp(log)} ` : ""} {stripTimestamp(log)}

)}
); }; export default CourseWorkspace;