From bd04401b12887911718f5edfd804278aa5a56f6a Mon Sep 17 00:00:00 2001 From: jacobxu666 Date: Tue, 4 Aug 2026 22:58:08 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=B6=E9=95=BF=E4=B8=AD=E5=BF=83=E9=87=8D?= =?UTF-8?q?=E7=BB=84:=E5=AD=A6=E4=B9=A0=E6=A6=82=E8=A7=88+=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E7=AE=A1=E7=90=86;=E5=AD=A9=E5=AD=90=E5=8F=AF?= =?UTF-8?q?=E5=A2=9E=E5=88=A0;=E8=AF=A6=E6=83=85=E9=A1=B5=E5=A4=B4?= =?UTF-8?q?=E9=83=A8=E5=8A=A0=E9=97=B4=E8=B7=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 顶部两标签:学习概览(合并查看已完成/需完成任务,日历点某天看明细) 与功能管理 - 功能管理含:任务管理(原任务模板日历)、课程管理(语法编辑+周测)、 孩子管理(增/删孩子)、设备管理(新设备激活+当前登录设备) - 孩子端英语知识点详情页头部增加元素间距,不再拥挤 Co-Authored-By: Claude Opus 4.8 (1M context) --- src/app/learn/[id]/page.tsx | 8 +++-- src/app/parent/actions.ts | 35 +++++++++++++++++++ src/app/parent/child-manage.tsx | 61 +++++++++++++++++++++++++++++++++ src/app/parent/dashboard.tsx | 44 ++++++++++++++++-------- 4 files changed, 130 insertions(+), 18 deletions(-) create mode 100644 src/app/parent/child-manage.tsx diff --git a/src/app/learn/[id]/page.tsx b/src/app/learn/[id]/page.tsx index 62651b0..fedc5e4 100644 --- a/src/app/learn/[id]/page.tsx +++ b/src/app/learn/[id]/page.tsx @@ -21,7 +21,7 @@ export default async function LessonPage({ params }: { params: Promise<{ id: str return (
-
+
← 课程地图 @@ -32,8 +32,10 @@ export default async function LessonPage({ params }: { params: Promise<{ id: str 已点亮 )} -

{point.title}

- {point.summary &&

🎯 {point.summary}

} +

+ {point.title} +

+ {point.summary &&

🎯 {point.summary}

}
diff --git a/src/app/parent/actions.ts b/src/app/parent/actions.ts index ec622b1..3f1179b 100644 --- a/src/app/parent/actions.ts +++ b/src/app/parent/actions.ts @@ -162,3 +162,38 @@ export async function revokeSession(sessionId: number) { await db.delete(sessions).where(eq(sessions.id, sessionId)); revalidatePath("/parent"); } + +// ===== 孩子管理:增/删 ===== +export async function addChild(formData: FormData): Promise { + const parent = await getSessionUser(); + if (!parent || parent.role !== "parent") return "请先以家长身份登录"; + const name = String(formData.get("name") ?? "").trim(); + if (!name) return "请填写孩子称呼"; + const [mx] = await db + .select({ m: sql`COALESCE(MAX(${users.sortOrder}), -1)` }) + .from(users) + .where(eq(users.familyId, parent.familyId)); + await db.insert(users).values({ + familyId: parent.familyId, + role: "child", + displayName: name, + sortOrder: mx.m + 1, + }); + revalidatePath("/parent"); + revalidatePath("/learn"); + return null; +} + +export async function removeChild(childId: number): Promise { + const parent = await getSessionUser(); + if (!parent || parent.role !== "parent") return; + const [row] = await db + .select({ id: users.id }) + .from(users) + .where(and(eq(users.id, childId), eq(users.familyId, parent.familyId), eq(users.role, "child"))) + .limit(1); + if (!row) return; + await db.delete(users).where(eq(users.id, childId)); + revalidatePath("/parent"); + revalidatePath("/learn"); +} diff --git a/src/app/parent/child-manage.tsx b/src/app/parent/child-manage.tsx new file mode 100644 index 0000000..88f73e4 --- /dev/null +++ b/src/app/parent/child-manage.tsx @@ -0,0 +1,61 @@ +"use client"; + +import { useState, useTransition } from "react"; +import { Trash2, UserPlus } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { addChild, removeChild } from "./actions"; + +// 孩子管理:增加/删除孩子 +export function ChildManage({ kids }: { kids: { id: number; name: string }[] }) { + const [name, setName] = useState(""); + const [msg, setMsg] = useState(""); + const [pending, startTransition] = useTransition(); + + const add = (e: React.FormEvent) => { + e.preventDefault(); + setMsg(""); + const fd = new FormData(); + fd.set("name", name); + startTransition(async () => { + const r = await addChild(fd); + if (r?.error) setMsg(r.error); + else { + setName(""); + setMsg("已添加,用孩子设备激活即可"); + } + }); + }; + + return ( +
+
+ {kids.map((k) => ( +
+ {k.name} + +
+ ))} +
+
+
+ setName(e.target.value)} placeholder="新孩子称呼,如:徐子惠" maxLength={20} /> +
+ +
+ {msg &&

{msg}

} +
+ ); +} diff --git a/src/app/parent/dashboard.tsx b/src/app/parent/dashboard.tsx index bcf6dc1..dbbfb1a 100644 --- a/src/app/parent/dashboard.tsx +++ b/src/app/parent/dashboard.tsx @@ -5,6 +5,7 @@ import { logout } from "@/app/login/actions"; import { BackgroundBlobs } from "@/components/background-blobs"; import { Button } from "@/components/ui/button"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { ChildManage } from "./child-manage"; import { DevicesTab } from "./devices-tab"; import { OverviewTab } from "./overview-tab"; import { TemplatesTab } from "./templates-tab"; @@ -26,7 +27,6 @@ export type OverviewData = { days: Record; }[]; }; - export type TemplatesData = { kids: { id: number; @@ -43,7 +43,6 @@ export type TemplatesData = { }[]; }[]; }; - export type DevicesData = { rows: { id: number; @@ -54,11 +53,20 @@ export type DevicesData = { isCurrent: boolean; }[]; }; - export type WeeklyRow = { childName: string; weekKey: string; score: number; passed: boolean }; const tabCls = "data-[state=active]:bg-white data-[state=active]:text-indigo-600 data-[state=active]:shadow-soft"; +function Section({ title, desc, children }: { title: string; desc?: string; children: React.ReactNode }) { + return ( +
+

{title}

+ {desc &&

{desc}

} +
{children}
+
+ ); +} + export function ParentDashboard({ overview, templates, @@ -85,26 +93,25 @@ export function ParentDashboard({
- + 学习概览 - - 任务模板 - - - 课程管理 + + 功能管理 - - - - -
+ + +
+ +
+ +
@@ -124,8 +131,15 @@ export function ParentDashboard({
+ + +
+ ({ id: k.id, name: k.name }))} /> +
+ +
({ id: k.id, name: k.name }))} /> - +