Compare commits

..

No commits in common. "3b8cbe680caa48d94833cba048670c62414d4146" and "e17871e53d7d5053638ee521a51ad4f4821ff258" have entirely different histories.

31 changed files with 78 additions and 153 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

View File

@ -394,7 +394,6 @@
DEVELOPMENT_ASSET_PATHS = "\"TeachMate/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"TeachMate/Preview Content\"";
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "小助教";
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
@ -432,7 +431,6 @@
DEVELOPMENT_ASSET_PATHS = "\"TeachMate/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"TeachMate/Preview Content\"";
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "小助教";
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

View File

@ -1,7 +1,6 @@
{ {
"images" : [ "images" : [
{ {
"filename" : "1024x1024.png",
"idiom" : "universal", "idiom" : "universal",
"platform" : "ios", "platform" : "ios",
"size" : "1024x1024" "size" : "1024x1024"
@ -13,7 +12,6 @@
"value" : "dark" "value" : "dark"
} }
], ],
"filename" : "1024x1024 3.png",
"idiom" : "universal", "idiom" : "universal",
"platform" : "ios", "platform" : "ios",
"size" : "1024x1024" "size" : "1024x1024"
@ -25,67 +23,56 @@
"value" : "tinted" "value" : "tinted"
} }
], ],
"filename" : "1024x1024 2.png",
"idiom" : "universal", "idiom" : "universal",
"platform" : "ios", "platform" : "ios",
"size" : "1024x1024" "size" : "1024x1024"
}, },
{ {
"filename" : "16x16.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "1x", "scale" : "1x",
"size" : "16x16" "size" : "16x16"
}, },
{ {
"filename" : "32x32.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "2x", "scale" : "2x",
"size" : "16x16" "size" : "16x16"
}, },
{ {
"filename" : "32x32 1.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "1x", "scale" : "1x",
"size" : "32x32" "size" : "32x32"
}, },
{ {
"filename" : "64x64.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "2x", "scale" : "2x",
"size" : "32x32" "size" : "32x32"
}, },
{ {
"filename" : "128x128.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "1x", "scale" : "1x",
"size" : "128x128" "size" : "128x128"
}, },
{ {
"filename" : "256x256.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "2x", "scale" : "2x",
"size" : "128x128" "size" : "128x128"
}, },
{ {
"filename" : "256x256 1.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "1x", "scale" : "1x",
"size" : "256x256" "size" : "256x256"
}, },
{ {
"filename" : "512x512.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "2x", "scale" : "2x",
"size" : "256x256" "size" : "256x256"
}, },
{ {
"filename" : "512x512 1.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "1x", "scale" : "1x",
"size" : "512x512" "size" : "512x512"
}, },
{ {
"filename" : "1024x1024 1.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "2x", "scale" : "2x",
"size" : "512x512" "size" : "512x512"

View File

@ -15,7 +15,7 @@ enum PreviewData {
// MARK: - // MARK: -
/// 2025 /// 2025
static let currentSemesterStartDate = Calendar.current.date(from: DateComponents(year: 2025, month: 2, day: 24))! static let currentSemesterStartDate = Calendar.current.date(from: DateComponents(year: 2025, month: 2, day: 17))!
/// ///
static let currentSemesterEndDate = Calendar.current.date(byAdding: .day, value: 18 * 7, to: currentSemesterStartDate)! static let currentSemesterEndDate = Calendar.current.date(byAdding: .day, value: 18 * 7, to: currentSemesterStartDate)!
@ -59,7 +59,7 @@ enum PreviewData {
/// ///
static let classes = [ static let classes = [
"云计算G23-1", "软件G22-3", "软件G22-4", "软件工程2班", "计算机1班", "计算机2班", "软件工程1班", "软件工程2班",
"网络工程1班", "人工智能1班", "数据科学1班", "信息安全1班" "网络工程1班", "人工智能1班", "数据科学1班", "信息安全1班"
] ]

View File

@ -7,23 +7,9 @@
import SwiftUI import SwiftUI
import SwiftData import SwiftData
#if os(macOS)
import AppKit
#endif
// Class to manage window title across the app
class WindowTitleManager: ObservableObject {
@Published var title: String = "TeachMate"
func updateTitle(_ newTitle: String) {
self.title = newTitle
}
}
@main @main
struct TeachMateApp: App { struct TeachMateApp: App {
@StateObject private var windowTitleManager = WindowTitleManager()
var sharedModelContainer: ModelContainer = { var sharedModelContainer: ModelContainer = {
let schema = Schema([ let schema = Schema([
Semester.self, Semester.self,
@ -42,17 +28,7 @@ struct TeachMateApp: App {
var body: some Scene { var body: some Scene {
WindowGroup { WindowGroup {
ContentView() ContentView()
.environmentObject(windowTitleManager)
.onReceive(windowTitleManager.$title) { newTitle in
#if os(macOS)
NSApp.windows.first?.title = newTitle
#endif
}
} }
.modelContainer(sharedModelContainer) .modelContainer(sharedModelContainer)
#if os(macOS)
.windowResizability(.contentSize)
.defaultSize(WindowSizeManager.WindowSizePreset.schedule.size)
#endif
} }
} }

View File

@ -8,7 +8,7 @@
import SwiftUI import SwiftUI
#if os(macOS) #if os(macOS)
// //
struct WindowSizeManager { struct WindowSizeManager {
enum WindowSizePreset { enum WindowSizePreset {
case calendar // - case calendar // -
@ -17,29 +17,52 @@ struct WindowSizeManager {
var size: CGSize { var size: CGSize {
switch self { switch self {
case .calendar: case .calendar:
return CGSize(width: 500, height: 800) return CGSize(width: 500, height: 800) //
case .schedule: case .schedule:
return CGSize(width: 1200, height: 680) return CGSize(width: 1200, height: 680) //
} }
} }
} }
}
// Scene便使 //
extension Scene { static func resizeWindow(to preset: WindowSizePreset) {
func windowIdealSize(_ preset: WindowSizeManager.WindowSizePreset) -> some Scene { guard let window = NSApplication.shared.windows.first else { return }
self.windowResizability(.contentSize)
.defaultSize(preset.size)
}
}
// View便 let newSize = preset.size
extension View { let currentFrame = window.frame
func idealFrame(for preset: WindowSizeManager.WindowSizePreset) -> some View {
self.frame( //
idealWidth: preset.size.width, let newOriginX = currentFrame.origin.x + (currentFrame.width - newSize.width) / 2
idealHeight: preset.size.height let newOriginY = currentFrame.origin.y + (currentFrame.height - newSize.height) / 2
let newFrame = NSRect(
x: newOriginX,
y: newOriginY,
width: newSize.width,
height: newSize.height
) )
//
window.animator().setFrame(newFrame, display: true, animate: true)
}
}
//
struct WindowSizeModifier: ViewModifier {
let preset: WindowSizeManager.WindowSizePreset
func body(content: Content) -> some View {
content
.onAppear {
WindowSizeManager.resizeWindow(to: preset)
}
}
}
// View便使
extension View {
func adjustWindowSize(to preset: WindowSizeManager.WindowSizePreset) -> some View {
modifier(WindowSizeModifier(preset: preset))
} }
} }
#endif #endif

View File

@ -11,7 +11,6 @@ import SwiftData
struct ContentView: View { struct ContentView: View {
@Environment(\.modelContext) private var modelContext @Environment(\.modelContext) private var modelContext
@Query private var semesters: [Semester] @Query private var semesters: [Semester]
@EnvironmentObject private var windowTitleManager: WindowTitleManager
// Add a state to track the selected navigation item // Add a state to track the selected navigation item
@State private var selectedNavItem: NavItem? = .schedule @State private var selectedNavItem: NavItem? = .schedule
@ -121,20 +120,14 @@ struct ContentView: View {
CourseScheduleView(semester: currentSemester) CourseScheduleView(semester: currentSemester)
.frame(maxWidth: .infinity, maxHeight: .infinity) .frame(maxWidth: .infinity, maxHeight: .infinity)
#if os(macOS) #if os(macOS)
.idealFrame(for: .schedule) .adjustWindowSize(to: .schedule)
#endif #endif
.onAppear {
windowTitleManager.updateTitle(selectedNavItem.rawValue)
}
} else if !semesters.isEmpty { } else if !semesters.isEmpty {
CourseScheduleView(semester: semesters[0]) CourseScheduleView(semester: semesters[0])
.frame(maxWidth: .infinity, maxHeight: .infinity) .frame(maxWidth: .infinity, maxHeight: .infinity)
#if os(macOS) #if os(macOS)
.idealFrame(for: .schedule) .adjustWindowSize(to: .schedule)
#endif #endif
.onAppear {
windowTitleManager.updateTitle(selectedNavItem.rawValue)
}
} else { } else {
VStack { VStack {
Text("请先添加学期") Text("请先添加学期")
@ -142,29 +135,20 @@ struct ContentView: View {
Text("在添加学期后,将自动显示课程表") Text("在添加学期后,将自动显示课程表")
.foregroundColor(.secondary) .foregroundColor(.secondary)
} }
.onAppear {
windowTitleManager.updateTitle(selectedNavItem.rawValue)
}
} }
case .calendar: case .calendar:
if let currentSemester = semesters.first(where: { $0.isCurrent }) { if let currentSemester = semesters.first(where: { $0.isCurrent }) {
AcademicCalendarView(semester: currentSemester) AcademicCalendarView(semester: currentSemester)
.frame(maxWidth: .infinity, maxHeight: .infinity) .frame(maxWidth: .infinity, maxHeight: .infinity)
#if os(macOS) #if os(macOS)
.idealFrame(for: .calendar) .adjustWindowSize(to: .calendar)
#endif #endif
.onAppear {
windowTitleManager.updateTitle(selectedNavItem.rawValue)
}
} else if !semesters.isEmpty { } else if !semesters.isEmpty {
AcademicCalendarView(semester: semesters[0]) AcademicCalendarView(semester: semesters[0])
.frame(maxWidth: .infinity, maxHeight: .infinity) .frame(maxWidth: .infinity, maxHeight: .infinity)
#if os(macOS) #if os(macOS)
.idealFrame(for: .calendar) .adjustWindowSize(to: .calendar)
#endif #endif
.onAppear {
windowTitleManager.updateTitle(selectedNavItem.rawValue)
}
} else { } else {
VStack { VStack {
Text("请先添加学期") Text("请先添加学期")
@ -172,20 +156,11 @@ struct ContentView: View {
Text("在添加学期后,将自动显示教学日历") Text("在添加学期后,将自动显示教学日历")
.foregroundColor(.secondary) .foregroundColor(.secondary)
} }
.onAppear {
windowTitleManager.updateTitle(selectedNavItem.rawValue)
}
} }
case .reminders: case .reminders:
Text("提醒事项内容") Text("提醒事项内容")
.onAppear {
windowTitleManager.updateTitle(selectedNavItem.rawValue)
}
case .courseManager: case .courseManager:
CourseManagerView() CourseManagerView()
.onAppear {
windowTitleManager.updateTitle(selectedNavItem.rawValue)
}
} }
} else if let semester = selectedSemester { } else if let semester = selectedSemester {
// Show semester detail // Show semester detail
@ -224,14 +199,9 @@ struct ContentView: View {
Spacer() Spacer()
} }
.padding() .padding()
.onAppear { .frame(maxWidth: .infinity, alignment: .leading)
windowTitleManager.updateTitle(semester.title)
}
} else { } else {
Text("选择一个项目以查看详情") Text("请选择一个项目")
.onAppear {
windowTitleManager.updateTitle("TeachMate")
}
} }
} }
} }
@ -258,5 +228,4 @@ struct ContentView: View {
#Preview { #Preview {
ContentView() ContentView()
.modelContainer(PreviewData.createSemesterContainer()) .modelContainer(PreviewData.createSemesterContainer())
.environmentObject(WindowTitleManager())
} }

View File

@ -64,7 +64,6 @@ struct CourseFormView: View {
setupInitialValues() setupInitialValues()
} }
} }
.frame(idealWidth: 600, idealHeight: 400)
} }
// Main content view // Main content view
@ -79,6 +78,7 @@ struct CourseFormView: View {
// Left side - Form content // Left side - Form content
formFieldsSection formFieldsSection
} }
.frame(minWidth: 600, idealWidth: 700, maxHeight: .infinity)
} }
} }

View File

@ -87,7 +87,7 @@ struct CourseManagerView: View {
mode: .edit(course), mode: .edit(course),
onSave: { _ in } onSave: { _ in }
) )
.frame(idealWidth: 600, idealHeight: 400) .frame(width: 600)
} else { } else {
CourseFormView( CourseFormView(
mode: .add, mode: .add,
@ -96,7 +96,7 @@ struct CourseManagerView: View {
selectedCourse = newCourse selectedCourse = newCourse
} }
) )
.frame(idealWidth: 600, idealHeight: 400) .frame(width: 600)
} }
} }
.onAppear { .onAppear {

View File

@ -39,34 +39,6 @@ struct CourseScheduleView: View {
courses.filter { $0.semester?.id == semester.id } courses.filter { $0.semester?.id == semester.id }
} }
//
private var hasWeekendCourses: Bool {
filteredCourses.contains { course in
course.sessions.contains { session in
session.weekday == 6 || session.weekday == 7
}
}
}
// 9/10
private var hasLateTimeslotCourses: Bool {
filteredCourses.contains { course in
course.sessions.contains { session in
session.timeSlot == 5 // 5 9/10
}
}
}
//
private var displayWeekdays: [Int] {
hasWeekendCourses ? Array(1...7) : Array(1...5)
}
//
private var displayTimeSlots: [TimeSlot] {
hasLateTimeslotCourses ? TimeSlot.defaultSlots : TimeSlot.defaultSlots.filter { $0.id != 5 }
}
// //
private func getCoursesForCell(weekday: Int, timeSlot: Int) -> [CourseModel] { private func getCoursesForCell(weekday: Int, timeSlot: Int) -> [CourseModel] {
return filteredCourses.filter { course in return filteredCourses.filter { course in
@ -90,7 +62,7 @@ struct CourseScheduleView: View {
headerRow headerRow
// //
ForEach(displayTimeSlots) { timeSlot in ForEach(TimeSlot.defaultSlots) { timeSlot in
courseRow(timeSlot: timeSlot) courseRow(timeSlot: timeSlot)
} }
} }
@ -162,8 +134,8 @@ struct CourseScheduleView: View {
.border(Color.gray.opacity(0.5), width: 0.5) .border(Color.gray.opacity(0.5), width: 0.5)
// //
ForEach(displayWeekdays, id: \.self) { weekday in ForEach(0..<7, id: \.self) { index in
Text(weekdays[weekday - 1]) Text(weekdays[index])
.font(.headline) .font(.headline)
.frame(width: dayCellWidth, height: headerHeight) .frame(width: dayCellWidth, height: headerHeight)
.background(Color.gray.opacity(0.3)) .background(Color.gray.opacity(0.3))
@ -188,7 +160,7 @@ struct CourseScheduleView: View {
.border(Color.gray.opacity(0.5), width: 0.5) .border(Color.gray.opacity(0.5), width: 0.5)
// //
ForEach(displayWeekdays, id: \.self) { weekday in ForEach(1...7, id: \.self) { weekday in
courseCell(weekday: weekday, timeSlot: timeSlot.id) courseCell(weekday: weekday, timeSlot: timeSlot.id)
} }
} }
@ -250,31 +222,31 @@ struct CourseScheduleView: View {
// //
private func addSampleCourses() { private func addSampleCourses() {
// 使 PreviewData var sampleCourses: [CourseModel] = []
let container = PreviewData.createContainer()
let fetchDescriptor = FetchDescriptor<CourseModel>()
let sampleCourses = try? container.mainContext.fetch(fetchDescriptor)
.filter { $0.semester?.id != semester.id } //
//
let aiCourse = CourseModel(name: "人工智能技术与应用", colorHex: "#FADBD8", isNew: false)
for course in sampleCourses ?? [] { aiCourse.addSession(weekday: 4, timeSlot: 5, location: "CMA101陈栋教室", schoolClass: "环艺G24-1,环艺G24-2,电竞G24-1")
// aiCourse.addSession(weekday: 5, timeSlot: 5, location: "CMA101陈栋教室", schoolClass: "视传G24-1,视传G24-2,视传G24-3")
let newCourse = CourseModel(name: course.name, colorHex: course.colorHex)
let dockerCourse = CourseModel(name: "容器云架构与运维", colorHex: "#D6EAF8", isNew: true)
dockerCourse.addSession(weekday: 3, timeSlot: 3, location: "XXGY402", schoolClass: "云计算G23-1")
dockerCourse.addSession(weekday: 5, timeSlot: 1, location: "XXGY404", schoolClass: "云计算G23-1")
// let networkCourse = CourseModel(name: "网络组建与维护", colorHex: "#FCF3CF", isNew: true)
for session in course.sessions { networkCourse.addSession(weekday: 1, timeSlot: 3, location: "XXA2305", schoolClass: "软件G23-3")
newCourse.addSession( networkCourse.addSession(weekday: 2, timeSlot: 3, location: "XXA2401", schoolClass: "软件G23-4")
weekday: session.weekday, networkCourse.addSession(weekday: 3, timeSlot: 2, location: "XXA2303", schoolClass: "软件G23-3")
timeSlot: session.timeSlot, networkCourse.addSession(weekday: 3, timeSlot: 4, location: "XXA2504", schoolClass: "软件G23-4")
location: session.location,
schoolClass: session.schoolCalss
)
}
// sampleCourses.append(aiCourse)
newCourse.semester = semester sampleCourses.append(dockerCourse)
modelContext.insert(newCourse) sampleCourses.append(networkCourse)
for course in sampleCourses {
course.semester = semester
modelContext.insert(course)
} }
} }

View File

@ -58,7 +58,6 @@ struct SessionFormView: View {
setupInitialValues() setupInitialValues()
} }
} }
.frame(idealWidth: 600, idealHeight: 400)
} }
// Main content view // Main content view
@ -77,6 +76,7 @@ struct SessionFormView: View {
Divider() Divider()
.padding(.vertical) .padding(.vertical)
} }
.frame(minWidth: 600, idealWidth: 700, maxHeight: .infinity)
} }
} }