学习闭环增强:错题本+每周能力检测+学习概览日历+模板日历+课程管理

- 错题本:所有检测错题自动入库,带详细讲解,可标记掌握,孩子端可查
- 每周能力检测:从已学知识点确定性组卷10题,计分记录,达标关联当日
  英语打卡,错题进错题本,家长可见成绩
- 家长"今日概览"改"学习概览",月日历展示每日打卡/语法点亮/周检测
- 任务模板改日历展示,灵活添加:某一天/每周X/本周/日期区间批量,可删
- 家长中心新增"课程管理"栏目(语法课程编辑+周测成绩),设备管理弱化为
  栏目内二级区块

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
jacobxu666
2026-08-03 23:39:52 +08:00
co-authored by Claude Opus 4.8
parent f98a21672d
commit d84d827202
18 changed files with 905 additions and 312 deletions
+11 -7
View File
@@ -76,8 +76,11 @@ export async function makeupTask(instanceId: number, done: boolean) {
const templateSchema = z.object({
childId: z.coerce.number().int().positive(),
dow: z.coerce.number().int().min(0).max(6),
dow: z.coerce.number().int().min(0).max(6).catch(0),
subject: z.enum(SUBJECT_VALUES).catch("other"),
mode: z.enum(["weekly", "date", "range", "week"]).catch("weekly"),
dateStart: z.string().optional(),
dateEnd: z.string().optional(),
title: z.string().trim().min(1, "任务内容不能为空").max(100, "最多 100 字"),
requiresPhoto: z.boolean(),
});
@@ -100,6 +103,9 @@ export async function addTemplate(formData: FormData) {
childId: formData.get("childId"),
dow: formData.get("dow"),
subject: formData.get("subject"),
mode: formData.get("mode"),
dateStart: formData.get("dateStart") || null,
dateEnd: formData.get("dateEnd") || null,
title: formData.get("title"),
requiresPhoto: formData.get("requiresPhoto") === "on",
});
@@ -109,17 +115,15 @@ export async function addTemplate(formData: FormData) {
const [maxRow] = await db
.select({ maxSort: sql<number>`COALESCE(MAX(${taskTemplates.sortOrder}), -1)` })
.from(taskTemplates)
.where(
and(
eq(taskTemplates.childUserId, parsed.data.childId),
eq(taskTemplates.dow, parsed.data.dow),
),
);
.where(eq(taskTemplates.childUserId, parsed.data.childId));
await db.insert(taskTemplates).values({
childUserId: parsed.data.childId,
dow: parsed.data.dow,
subject: parsed.data.subject,
mode: parsed.data.mode,
dateStart: parsed.data.dateStart || null,
dateEnd: parsed.data.dateEnd || null,
title: parsed.data.title,
requiresPhoto: parsed.data.requiresPhoto,
sortOrder: maxRow.maxSort + 1,