家长端课程管理移除跟读录音、语法检测分数、孩子错题本卡片,只保留课程编辑与每周检测成绩
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
f80a931ef0
commit
ab84bec1b4
@@ -9,7 +9,6 @@ import { ChildManage } from "./child-manage";
|
|||||||
import { DevicesTab } from "./devices-tab";
|
import { DevicesTab } from "./devices-tab";
|
||||||
import { OverviewTab } from "./overview-tab";
|
import { OverviewTab } from "./overview-tab";
|
||||||
import { TemplatesTab } from "./templates-tab";
|
import { TemplatesTab } from "./templates-tab";
|
||||||
import { PlayBtn } from "@/components/play-audio";
|
|
||||||
|
|
||||||
export type DayCell = {
|
export type DayCell = {
|
||||||
done: number;
|
done: number;
|
||||||
@@ -57,16 +56,6 @@ export type DevicesData = {
|
|||||||
}[];
|
}[];
|
||||||
};
|
};
|
||||||
export type WeeklyRow = { childName: string; weekKey: string; score: number; passed: boolean };
|
export type WeeklyRow = { childName: string; weekKey: string; score: number; passed: boolean };
|
||||||
export type RecordingRow = { childName: string; word: string; url: string; at: string };
|
|
||||||
export type GrammarTestRow = { childName: string; title: string; score: number; passed: boolean; at: string };
|
|
||||||
export type WrongItemRow = {
|
|
||||||
childName: string;
|
|
||||||
stem: string;
|
|
||||||
userAnswer: string;
|
|
||||||
correctAnswer: string;
|
|
||||||
sourceTitle: string;
|
|
||||||
mastered: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
const tabCls = "data-[state=active]:bg-white data-[state=active]:text-indigo-600 data-[state=active]:shadow-soft";
|
const tabCls = "data-[state=active]:bg-white data-[state=active]:text-indigo-600 data-[state=active]:shadow-soft";
|
||||||
|
|
||||||
@@ -85,17 +74,11 @@ export function ParentDashboard({
|
|||||||
templates,
|
templates,
|
||||||
devices,
|
devices,
|
||||||
weekly,
|
weekly,
|
||||||
recordings,
|
|
||||||
grammarTests,
|
|
||||||
wrongItems,
|
|
||||||
}: {
|
}: {
|
||||||
overview: OverviewData;
|
overview: OverviewData;
|
||||||
templates: TemplatesData;
|
templates: TemplatesData;
|
||||||
devices: DevicesData;
|
devices: DevicesData;
|
||||||
weekly: WeeklyRow[];
|
weekly: WeeklyRow[];
|
||||||
recordings: RecordingRow[];
|
|
||||||
grammarTests: GrammarTestRow[];
|
|
||||||
wrongItems: WrongItemRow[];
|
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<main className="relative mx-auto max-w-3xl space-y-5 p-4 pb-10">
|
<main className="relative mx-auto max-w-3xl space-y-5 p-4 pb-10">
|
||||||
@@ -149,45 +132,6 @@ export function ParentDashboard({
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="rounded-xl border border-slate-100 bg-white p-4 shadow-soft sm:col-span-2">
|
|
||||||
<p className="font-bold text-slate-900">🎙️ 单词跟读录音</p>
|
|
||||||
<div className="mt-2 flex flex-wrap gap-2">
|
|
||||||
{recordings.length === 0 && <p className="text-xs text-muted-foreground">暂无跟读录音</p>}
|
|
||||||
{recordings.map((r, i) => (
|
|
||||||
<div key={i} className="flex items-center gap-1.5 rounded-full bg-slate-50 px-2.5 py-1 text-sm ring-1 ring-slate-100">
|
|
||||||
<span className="text-xs text-muted-foreground">{r.childName}</span>
|
|
||||||
<span className="font-medium text-slate-800">{r.word}</span>
|
|
||||||
<PlayBtn src={r.url} className="size-6" />
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="rounded-xl border border-slate-100 bg-white p-4 shadow-soft">
|
|
||||||
<p className="font-bold text-slate-900">📘 英语语法检测</p>
|
|
||||||
<div className="mt-2 space-y-1">
|
|
||||||
{grammarTests.length === 0 && <p className="text-xs text-muted-foreground">暂无检测记录</p>}
|
|
||||||
{grammarTests.map((g, i) => (
|
|
||||||
<p key={i} className="truncate text-xs text-slate-600">
|
|
||||||
{g.childName} · {g.title} · {g.score} 分 {g.passed ? "✅" : "❌"}
|
|
||||||
</p>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="rounded-xl border border-slate-100 bg-white p-4 shadow-soft">
|
|
||||||
<p className="font-bold text-slate-900">📒 孩子错题本</p>
|
|
||||||
<div className="mt-2 space-y-2">
|
|
||||||
{wrongItems.length === 0 && <p className="text-xs text-muted-foreground">暂无错题</p>}
|
|
||||||
{wrongItems.map((w, i) => (
|
|
||||||
<div key={i} className="rounded-lg bg-slate-50 p-2 text-xs">
|
|
||||||
<p className="font-medium text-slate-800">
|
|
||||||
{w.childName} · {w.stem}
|
|
||||||
</p>
|
|
||||||
<p className="mt-0.5 text-rose-600">答:{w.userAnswer || "未作答"}</p>
|
|
||||||
<p className="text-emerald-700">正:{w.correctAnswer}</p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
|
|||||||
@@ -191,90 +191,12 @@ export default async function ParentPage() {
|
|||||||
passed: w.passed,
|
passed: w.passed,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// ===== 单词跟读录音(课程管理回放) =====
|
|
||||||
const recRows = await db
|
|
||||||
.select({
|
|
||||||
childName: users.displayName,
|
|
||||||
word: wordRecording.word,
|
|
||||||
ossKey: wordRecording.ossKey,
|
|
||||||
createdAt: wordRecording.createdAt,
|
|
||||||
})
|
|
||||||
.from(wordRecording)
|
|
||||||
.innerJoin(users, eq(users.id, wordRecording.childUserId))
|
|
||||||
.where(eq(users.familyId, parent.familyId))
|
|
||||||
.orderBy(desc(wordRecording.createdAt))
|
|
||||||
.limit(20);
|
|
||||||
const recordings = recRows.map((r) => ({
|
|
||||||
childName: r.childName,
|
|
||||||
word: r.word,
|
|
||||||
url: viewUrl(r.ossKey),
|
|
||||||
at: dstr(r.createdAt),
|
|
||||||
}));
|
|
||||||
|
|
||||||
// ===== 英语语法检测明细 =====
|
|
||||||
const grammarRows = await db
|
|
||||||
.select({
|
|
||||||
childName: users.displayName,
|
|
||||||
title: grammarPoints.title,
|
|
||||||
score: grammarProgress.score,
|
|
||||||
passed: grammarProgress.passed,
|
|
||||||
completedAt: grammarProgress.completedAt,
|
|
||||||
})
|
|
||||||
.from(grammarProgress)
|
|
||||||
.innerJoin(users, eq(users.id, grammarProgress.childUserId))
|
|
||||||
.innerJoin(grammarPoints, eq(grammarPoints.pointId, grammarProgress.pointId))
|
|
||||||
.where(eq(users.familyId, parent.familyId))
|
|
||||||
.orderBy(desc(grammarProgress.completedAt))
|
|
||||||
.limit(20);
|
|
||||||
const grammarTests = grammarRows.map((g) => ({
|
|
||||||
childName: g.childName,
|
|
||||||
title: g.title,
|
|
||||||
score: g.score ?? 0,
|
|
||||||
passed: g.passed,
|
|
||||||
at: g.completedAt ? dstr(g.completedAt) : "",
|
|
||||||
}));
|
|
||||||
|
|
||||||
// ===== 孩子错题数据 =====
|
|
||||||
const wrongRows = await db
|
|
||||||
.select({
|
|
||||||
childName: users.displayName,
|
|
||||||
question: wrongBook.question,
|
|
||||||
userAnswer: wrongBook.userAnswer,
|
|
||||||
correctAnswer: wrongBook.correctAnswer,
|
|
||||||
sourceTitle: wrongBook.sourceTitle,
|
|
||||||
mastered: wrongBook.mastered,
|
|
||||||
})
|
|
||||||
.from(wrongBook)
|
|
||||||
.innerJoin(users, eq(users.id, wrongBook.childUserId))
|
|
||||||
.where(eq(users.familyId, parent.familyId))
|
|
||||||
.orderBy(desc(wrongBook.createdAt))
|
|
||||||
.limit(30);
|
|
||||||
const wrongItems = wrongRows.map((w) => {
|
|
||||||
let stem = "";
|
|
||||||
try {
|
|
||||||
stem = (JSON.parse(w.question) as { stem?: string }).stem ?? "";
|
|
||||||
} catch {
|
|
||||||
stem = "";
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
childName: w.childName,
|
|
||||||
stem,
|
|
||||||
userAnswer: w.userAnswer ?? "",
|
|
||||||
correctAnswer: w.correctAnswer ?? "",
|
|
||||||
sourceTitle: w.sourceTitle ?? "",
|
|
||||||
mastered: w.mastered,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ParentDashboard
|
<ParentDashboard
|
||||||
overview={overview}
|
overview={overview}
|
||||||
templates={templates}
|
templates={templates}
|
||||||
devices={devices}
|
devices={devices}
|
||||||
weekly={weekly}
|
weekly={weekly}
|
||||||
recordings={recordings}
|
|
||||||
grammarTests={grammarTests}
|
|
||||||
wrongItems={wrongItems}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user