// // CourseManagerView.swift // TeachMate // // Created by Hongli on 2025/3/17. // import SwiftUI import SwiftData struct CourseManagerView: View { @Environment(\.modelContext) private var modelContext @Query(sort: \CourseModel.name) private var courses: [CourseModel] @Query(sort: \Semester.title) private var semesters: [Semester] @State private var selectedCourse: CourseModel? @State private var showingCourseForm = false @State private var courseToEdit: CourseModel? var filteredCourses: [CourseModel] { var result = courses // 按当前学期筛选 if let currentSemester = semesters.first(where: { $0.isCurrent }) { result = result.filter { $0.semester?.id == currentSemester.id } } return result } var body: some View { NavigationSplitView { VStack(spacing: 0) { // 课程列表 List(selection: $selectedCourse) { ForEach(filteredCourses) { course in CourseRow(course: course) .tag(course) } .onDelete(perform: deleteCourses) } .listStyle(.plain) .overlay { if filteredCourses.isEmpty { ContentUnavailableView { Label("没有课程", systemImage: "book.closed") } description: { Text("点击右上角的 + 按钮添加课程") } } } } .navigationTitle("课程管理") .toolbar { ToolbarItem(placement: .automatic) { Button(action: { courseToEdit = nil showingCourseForm = true }) { Image(systemName: "plus") } } ToolbarItem(placement: .automatic) { Button(action: { if let course = selectedCourse { courseToEdit = course showingCourseForm = true } }) { Image(systemName: "pencil") } .disabled(selectedCourse == nil) } } } detail: { if let course = selectedCourse { CourseDetailView(course: course) } else { Text("请选择一门课程查看详情") .foregroundColor(.secondary) } } .sheet(isPresented: $showingCourseForm) { if let course = courseToEdit { CourseFormView( mode: .edit(course), onSave: { _ in } ) .frame(idealWidth: 600, idealHeight: 400) } else { CourseFormView( mode: .add, onSave: { newCourse in modelContext.insert(newCourse) selectedCourse = newCourse } ) .frame(idealWidth: 600, idealHeight: 400) } } .onAppear { // 如果没有选中的课程且有可用课程,自动选择第一个课程(对预览特别有用) if selectedCourse == nil && !filteredCourses.isEmpty { selectedCourse = filteredCourses.first } } } private func deleteCourses(at offsets: IndexSet) { for index in offsets { let course = filteredCourses[index] modelContext.delete(course) } } } // 课程行视图 struct CourseRow: View { let course: CourseModel var body: some View { HStack { // 颜色标记 RoundedRectangle(cornerRadius: 4) .fill(course.backgroundColor) .frame(width: 4, height: 40) VStack(alignment: .leading, spacing: 4) { HStack { Text(course.name) .font(.headline) } Text("周 \(course.sessions.count) 节") .font(.caption) .foregroundColor(.secondary) } .padding(.leading, 8) Spacer() } .padding(.vertical, 4) } } // 课程详情视图 struct CourseDetailView: View { @Environment(\.modelContext) private var modelContext let course: CourseModel @State private var showingSessionForm = false @State private var sessionToEdit: ClassSession? // 星期标题 private let weekdays = ["一", "二", "三", "四", "五", "六", "日"] var body: some View { VStack(alignment: .leading, spacing: 16) { // 课程基本信息 HStack(alignment: .center) { VStack(alignment: .leading, spacing: 4) { HStack { Text(course.name) .font(.title) .fontWeight(.bold) } if let semester = course.semester { Text("学期: \(semester.title)") .foregroundColor(.secondary) } } Spacer() // 颜色标记 Circle() .fill(course.backgroundColor) .frame(width: 24, height: 24) } .padding(.horizontal) Divider() // 课时列表 VStack(alignment: .leading, spacing: 8) { HStack { Text("课时列表") .font(.headline) Spacer() Button(action: { sessionToEdit = nil showingSessionForm = true }) { Label("添加课时", systemImage: "plus.circle") } .buttonStyle(.borderless) } .padding(.horizontal) if course.sessions.isEmpty { Text("没有课时信息") .foregroundColor(.secondary) .frame(maxWidth: .infinity, alignment: .center) .padding() } else { List { ForEach(course.sessions, id: \.self) { session in SessionRow(session: session, weekdays: weekdays) .contextMenu { Button(action: { sessionToEdit = session showingSessionForm = true }) { Label("编辑", systemImage: "pencil") } Button(role: .destructive, action: { deleteSession(session) }) { Label("删除", systemImage: "trash") } } } } .listStyle(.plain) } } } .padding(.vertical) .sheet(isPresented: $showingSessionForm) { if let session = sessionToEdit { SessionFormView(mode: .edit(session), course: course) } else { SessionFormView(mode: .add, course: course) } } } private func deleteSession(_ session: ClassSession) { modelContext.delete(session) } } // 课时行视图 struct SessionRow: View { let session: ClassSession let weekdays: [String] var body: some View { VStack(alignment: .leading, spacing: 8) { HStack { Text("周\(weekdays[session.weekday - 1])") .font(.headline) Text("第\(session.timeSlot)节") .foregroundColor(.secondary) Spacer() Text(timeSlotString(for: session.timeSlot)) .font(.caption) .foregroundColor(.secondary) } HStack { Image(systemName: "mappin.and.ellipse") .foregroundColor(.secondary) .font(.caption) Text(session.location) .font(.subheadline) Spacer() if !session.schoolCalss.isEmpty { Text(session.schoolCalss) .font(.caption) .padding(.horizontal, 6) .padding(.vertical, 2) .background(Color.gray.opacity(0.2)) .cornerRadius(4) } } } .padding(.vertical, 4) } private func timeSlotString(for slot: Int) -> String { let slots = TimeSlot.defaultSlots if slot >= 1 && slot <= slots.count { return slots[slot - 1].timeRange } return "" } } #Preview("课程管理") { CourseManagerView() .modelContainer(PreviewData.createContainer()) .onAppear { // 预览时给一些时间让数据加载 DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { // 这个空的闭包会触发视图刷新,有助于在预览中显示数据 } } }