新增英语单词打卡模块:3694词/247天,发音/详情/跟读录音/每日检测
- 数据源:解析 3500 词 PDF 得 3694 词,按每天 15 词分 247 天,覆盖全部单词 - 孩子端 /words 地图 + 当天学习页:点词标准发音(有道语音)、详情(释义/ 形近词/例句)、学会标记、跟读录音(MediaRecorder 直传 OSS)、每日检测 (选释义,达标点亮并计入英语打卡) - 家长端课程管理可回放孩子跟读录音 - 形近词运行时编辑距离计算;例句按需取词典 API Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
38a64b4ec1
commit
2bbf349e0c
@@ -54,6 +54,7 @@ export type DevicesData = {
|
||||
}[];
|
||||
};
|
||||
export type WeeklyRow = { childName: string; weekKey: string; score: number; passed: boolean };
|
||||
export type RecordingRow = { childName: string; word: string; url: string; at: string };
|
||||
|
||||
const tabCls = "data-[state=active]:bg-white data-[state=active]:text-indigo-600 data-[state=active]:shadow-soft";
|
||||
|
||||
@@ -72,11 +73,13 @@ export function ParentDashboard({
|
||||
templates,
|
||||
devices,
|
||||
weekly,
|
||||
recordings,
|
||||
}: {
|
||||
overview: OverviewData;
|
||||
templates: TemplatesData;
|
||||
devices: DevicesData;
|
||||
weekly: WeeklyRow[];
|
||||
recordings: RecordingRow[];
|
||||
}) {
|
||||
return (
|
||||
<main className="relative mx-auto max-w-3xl space-y-5 p-4 pb-10">
|
||||
@@ -130,6 +133,19 @@ export function ParentDashboard({
|
||||
))}
|
||||
</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 space-y-2">
|
||||
{recordings.length === 0 && <p className="text-xs text-muted-foreground">暂无跟读录音</p>}
|
||||
{recordings.map((r, i) => (
|
||||
<div key={i} className="flex items-center gap-2 text-sm">
|
||||
<span className="w-16 shrink-0 text-xs text-muted-foreground">{r.childName}</span>
|
||||
<span className="w-20 shrink-0 font-medium text-slate-800">{r.word}</span>
|
||||
<audio controls src={r.url} className="h-8 flex-1" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
|
||||
+25
-1
@@ -11,10 +11,12 @@ import {
|
||||
grammarProgress,
|
||||
weeklyTest,
|
||||
wrongBook,
|
||||
wordRecording,
|
||||
} from "@/db/schema";
|
||||
import { SESSION_COOKIE, getSessionUser } from "@/lib/auth";
|
||||
import { addDays, dowOf, todayStr } from "@/lib/dates";
|
||||
import { passedCount, totalPoints } from "@/lib/grammar";
|
||||
import { viewUrl } from "@/lib/oss";
|
||||
import {
|
||||
ParentDashboard,
|
||||
type DevicesData,
|
||||
@@ -168,5 +170,27 @@ export default async function ParentPage() {
|
||||
passed: w.passed,
|
||||
}));
|
||||
|
||||
return <ParentDashboard overview={overview} templates={templates} devices={devices} weekly={weekly} />;
|
||||
// ===== 单词跟读录音(课程管理回放) =====
|
||||
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),
|
||||
}));
|
||||
|
||||
return (
|
||||
<ParentDashboard overview={overview} templates={templates} devices={devices} weekly={weekly} recordings={recordings} />
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user