From f6e969f15c38a9a5e834987eabeaf6bf7e4f4405 Mon Sep 17 00:00:00 2001 From: aaron <> Date: Mon, 27 Apr 2026 11:04:00 +0800 Subject: [PATCH] 1 --- backend/app/services/schedule_service.py | 18 +++- frontend/src/app/(app)/schedule/page.tsx | 100 ++++++++++++---------- frontend/src/components/calendar-view.tsx | 8 +- 3 files changed, 77 insertions(+), 49 deletions(-) diff --git a/backend/app/services/schedule_service.py b/backend/app/services/schedule_service.py index e7f4a60..67cba9c 100644 --- a/backend/app/services/schedule_service.py +++ b/backend/app/services/schedule_service.py @@ -11,16 +11,22 @@ from app.services.notification_service import create_notifications_for_class async def create_schedule( db: AsyncSession, class_id: int, data: ScheduleCreate ) -> Schedule: + payload = data.model_dump() + if payload.get("type") == "deadline" and payload.get("end_time") is not None: + # Deadlines are anchored on the due time only; reuse start_time as the sort/query key. + payload["start_time"] = payload["end_time"] + item = Schedule( class_id=class_id, - **data.model_dump(), + **payload, ) db.add(item) await db.commit() await db.refresh(item) # Send notifications + email to class members - time_str = data.start_time.strftime("%Y-%m-%d %H:%M") + effective_time = payload["end_time"] if payload.get("type") == "deadline" and payload.get("end_time") else payload["start_time"] + time_str = effective_time.strftime("%Y-%m-%d %H:%M") location_info = f" · {data.location}" if data.location else "" await create_notifications_for_class( db, class_id, "schedule", f"新排期: {data.title}", @@ -37,7 +43,13 @@ async def create_schedule( async def update_schedule( db: AsyncSession, item: Schedule, data: ScheduleUpdate ) -> Schedule: - for field, value in data.model_dump(exclude_unset=True).items(): + payload = data.model_dump(exclude_unset=True) + next_type = payload.get("type", item.type) + next_end_time = payload.get("end_time", item.end_time) + if next_type == "deadline" and next_end_time is not None: + payload["start_time"] = next_end_time + + for field, value in payload.items(): setattr(item, field, value) await db.commit() await db.refresh(item) diff --git a/frontend/src/app/(app)/schedule/page.tsx b/frontend/src/app/(app)/schedule/page.tsx index 2d50673..79a5c2a 100644 --- a/frontend/src/app/(app)/schedule/page.tsx +++ b/frontend/src/app/(app)/schedule/page.tsx @@ -51,6 +51,10 @@ export default function SchedulePage() { const [formEndTime, setFormEndTime] = useState(""); const [formLocation, setFormLocation] = useState(""); const [formDesc, setFormDesc] = useState(""); + const isDeadlineType = formType === "deadline"; + + const getDisplayTime = (item: ScheduleItem) => + item.type === "deadline" && item.end_time ? item.end_time : item.start_time; const loadData = useCallback(async () => { if (!activeClassId) { @@ -82,10 +86,10 @@ export default function SchedulePage() { void loadData(); }, [loadData]); - const getCountdown = (startTime: string) => { - const diff = new Date(startTime).getTime() - Date.now(); + const getCountdown = (dateTime: string) => { + const diff = new Date(dateTime).getTime() - Date.now(); const days = Math.ceil(diff / (1000 * 60 * 60 * 24)); - if (days <= 0) return { text: "已开始", urgent: false }; + if (days <= 0) return { text: "已截止", urgent: false }; if (days === 1) return { text: "明天", urgent: true }; if (days <= 3) return { text: `${days}天后`, urgent: true }; if (days <= 7) return { text: `${days}天后`, urgent: false }; @@ -93,28 +97,23 @@ export default function SchedulePage() { }; const handleSubmit = async () => { - if (!formTitle.trim() || !formStartTime) return; + const primaryTime = isDeadlineType ? formEndTime : formStartTime; + if (!formTitle.trim() || !primaryTime) return; setSubmitting(true); try { + const payload = { + type: formType, + title: formTitle, + start_time: isDeadlineType ? primaryTime : formStartTime, + end_time: isDeadlineType ? primaryTime : formEndTime || null, + location: isDeadlineType ? null : formLocation || null, + description: formDesc || null, + }; if (editingId) { - await putAPI(`/api/schedule/${editingId}`, { - type: formType, - title: formTitle, - start_time: formStartTime, - end_time: formEndTime || null, - location: formLocation || null, - description: formDesc || null, - }); + await putAPI(`/api/schedule/${editingId}`, payload); toast.success("排期已更新"); } else { - await postAPI(`/api/schedule/?class_id=${activeClassId}`, { - type: formType, - title: formTitle, - start_time: formStartTime, - end_time: formEndTime || null, - location: formLocation || null, - description: formDesc || null, - }); + await postAPI(`/api/schedule/?class_id=${activeClassId}`, payload); toast.success("排期已创建"); } setDialogOpen(false); @@ -137,9 +136,10 @@ export default function SchedulePage() { const dt = new Date(d); return `${dt.getFullYear()}-${pad(dt.getMonth() + 1)}-${pad(dt.getDate())}T${pad(dt.getHours())}:${pad(dt.getMinutes())}`; }; - setFormStartTime(fmt(item.start_time)); - setFormEndTime(item.end_time ? fmt(item.end_time) : ""); - setFormLocation(item.location || ""); + const effectiveTime = item.type === "deadline" && item.end_time ? item.end_time : item.start_time; + setFormStartTime(item.type === "deadline" ? "" : fmt(item.start_time)); + setFormEndTime(item.type === "deadline" ? fmt(effectiveTime) : item.end_time ? fmt(item.end_time) : ""); + setFormLocation(item.type === "deadline" ? "" : item.location || ""); setFormDesc(item.description || ""); setDialogOpen(true); }; @@ -220,29 +220,42 @@ export default function SchedulePage() { value={formTitle} onChange={(e) => setFormTitle(e.target.value)} /> -
+ {isDeadlineType ? (
- - setFormStartTime(e.target.value)} - /> -
-
- + setFormEndTime(e.target.value)} />
-
- setFormLocation(e.target.value)} - /> + ) : ( + <> +
+
+ + setFormStartTime(e.target.value)} + /> +
+
+ + setFormEndTime(e.target.value)} + /> +
+
+ setFormLocation(e.target.value)} + /> + + )}