This commit is contained in:
grtsinry43 2025-05-12 00:13:26 +08:00
parent 20d9a596d7
commit 502aaf51f0
Signed by: grtsinry43
GPG Key ID: F3305FB3A978C934
2 changed files with 1132 additions and 1478 deletions

View File

@ -1,11 +1,11 @@
import SwiftUI import SwiftUI
import Shared import Shared
// MARK: - BottomBar
struct BottomBar: View { struct BottomBar: View {
// Use meaningful tab names
@State private var selectedTab: Tab = .today @State private var selectedTab: Tab = .today
// Define Tabs with associated icons and names
enum Tab: CaseIterable { enum Tab: CaseIterable {
case today, weekly, settings case today, weekly, settings
@ -28,69 +28,61 @@ struct BottomBar: View {
var body: some View { var body: some View {
TabView(selection: $selectedTab) { TabView(selection: $selectedTab) {
// Use ContentView for the "Today" tab
ContentView() ContentView()
.tag(Tab.today) .tag(Tab.today)
.tabItem { Label(Tab.today.title, systemImage: Tab.today.iconName) } .tabItem { Label(Tab.today.title, systemImage: Tab.today.iconName) }
// Placeholder for Weekly view
WeeklyView() WeeklyView()
.tag(Tab.weekly) .tag(Tab.weekly)
.tabItem { Label(Tab.weekly.title, systemImage: Tab.weekly.iconName) } .tabItem { Label(Tab.weekly.title, systemImage: Tab.weekly.iconName) }
// Placeholder for Settings view
SettingsView() SettingsView()
.tag(Tab.settings) .tag(Tab.settings)
.tabItem { Label(Tab.settings.title, systemImage: Tab.settings.iconName) } .tabItem { Label(Tab.settings.title, systemImage: Tab.settings.iconName) }
} }
.accentColor(.purple) // Example: Set a custom accent color for the tab bar .accentColor(.purple)
} }
} }
// --- Weekly View --- // MARK: - WeeklyView
struct WeeklyView: View { struct WeeklyView: View {
// Placeholder data for weekly usage (e.g., hours per day)
let weeklyData: [Double] = [3.5, 4.2, 5.1, 2.8, 6.0, 7.5, 4.8] let weeklyData: [Double] = [3.5, 4.2, 5.1, 2.8, 6.0, 7.5, 4.8]
let days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] let days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
let goalHours: Double = 8.0 // Example daily goal let goalHours: Double = 8.0
// Calculate max value for chart scaling
var maxValue: Double { var maxValue: Double {
(weeklyData.max() ?? goalHours) * 1.1 // Add 10% buffer (weeklyData.max() ?? goalHours) * 1.1
} }
var body: some View { var body: some View {
NavigationView { NavigationView {
// Use List for standard iOS layout
List { List {
Section("Usage Trend") { Section("Usage Trend") {
// Bar Chart Visualization HStack(alignment: .bottom, spacing: 15) {
HStack(alignment: .bottom, spacing: 15) { // Increased spacing
ForEach(0..<weeklyData.count, id: \.self) { index in ForEach(0..<weeklyData.count, id: \.self) { index in
VStack(spacing: 4) { // Spacing within the bar stack VStack(spacing: 4) {
Text(String(format: "%.1fh", weeklyData[index])) Text(String(format: "%.1fh", weeklyData[index]))
.font(.caption) .font(.caption)
.foregroundColor(.accentColor) .foregroundColor(.accentColor)
.lineLimit(1) .lineLimit(1)
.rotationEffect(.degrees(-90)) // Rotate text for better fit if needed .rotationEffect(.degrees(-90))
.offset(y: -25) // Adjust offset if rotated .offset(y: -25)
.frame(height: 50) // Give space for text .frame(height: 50)
Rectangle() Rectangle()
.fill(weeklyData[index] > goalHours ? Color.orange : Color.accentColor) // Highlight days over goal .fill(weeklyData[index] > goalHours ? Color.orange : Color.accentColor)
// Scale height relative to the max value .frame(height: max(1, CGFloat(weeklyData[index] / maxValue) * 150))
.frame(height: max(1, CGFloat(weeklyData[index] / maxValue) * 150)) // Ensure min height, scale based on max .cornerRadius(5)
.cornerRadius(5) // Rounded corners for bars
Text(days[index]) Text(days[index])
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundColor(.secondary)
} }
.frame(maxWidth: .infinity) // Allow bars to take equal width .frame(maxWidth: .infinity)
} }
} }
.frame(height: 220) // Adjust overall chart height .frame(height: 220)
.padding(.vertical) // Add padding around the chart .padding(.vertical)
} }
Section("Summary") { Section("Summary") {
@ -114,33 +106,54 @@ struct WeeklyView: View {
} }
} }
} }
.listStyle(.insetGrouped) // Use inset grouped style .listStyle(.insetGrouped)
.navigationTitle("Weekly Stats") .navigationTitle("Weekly Stats")
} }
.navigationViewStyle(StackNavigationViewStyle()) .navigationViewStyle(StackNavigationViewStyle())
} }
// Helper function to calculate weekly average
func calculateWeeklyAverage() -> String { func calculateWeeklyAverage() -> String {
let totalHours = weeklyData.reduce(0, +) let totalHours = weeklyData.reduce(0, +)
let averageHours = totalHours / Double(weeklyData.count) let averageHours = totalHours / Double(weeklyData.count)
let formatter = DateComponentsFormatter() let formatter = DateComponentsFormatter()
formatter.allowedUnits = [.hour, .minute] formatter.allowedUnits = [.hour, .minute]
formatter.unitsStyle = .abbreviated formatter.unitsStyle = .abbreviated
// Convert hours to seconds for formatter
return formatter.string(from: TimeInterval(averageHours * 3600)) ?? "0m" return formatter.string(from: TimeInterval(averageHours * 3600)) ?? "0m"
} }
// Helper property for days over goal
var daysOverGoalCount: Int { var daysOverGoalCount: Int {
weeklyData.filter { $0 > goalHours }.count weeklyData.filter { $0 > goalHours }.count
} }
} }
// --- Settings View --- // MARK: - GreetingViewModel
class GreetingViewModel: ObservableObject {
@Published var greetingText = "加载中..."
private let greeting = Greeting()
func loadGreeting() {
greetingText = "加载中..."
Task {
do {
let result = try await greeting.greet()
await MainActor.run {
self.greetingText = result
}
} catch {
await MainActor.run {
self.greetingText = "错误: \(error.localizedDescription)"
}
}
}
}
}
// MARK: - SettingsView
struct SettingsView: View { struct SettingsView: View {
@StateObject private var viewModel = GreetingViewModel()
@State private var usageReminders = true @State private var usageReminders = true
@State private var darkModeEnabled = false // Example state @State private var darkModeEnabled = false
@State private var selectedLimit = "No Limit" @State private var selectedLimit = "No Limit"
let appLimits = ["No Limit", "1 hour", "2 hours", "Custom"] let appLimits = ["No Limit", "1 hour", "2 hours", "Custom"]
@ -149,7 +162,7 @@ struct SettingsView: View {
Form { Form {
Section("Notifications") { Section("Notifications") {
Toggle("Usage Reminders", isOn: $usageReminders) Toggle("Usage Reminders", isOn: $usageReminders)
Toggle("Goal Achievement Alerts", isOn: .constant(false)) // Added toggle Toggle("Goal Achievement Alerts", isOn: .constant(false))
} }
Section("Usage Limits") { Section("Usage Limits") {
@ -158,11 +171,11 @@ struct SettingsView: View {
Text($0) Text($0)
} }
} }
NavigationLink("App Specific Limits", destination: Text("App Limits Detail (Placeholder)")) // Added link NavigationLink("App Specific Limits", destination: Text("App Limits Detail (Placeholder)"))
} }
Section("Appearance") { Section("Appearance") {
Toggle("Dark Mode", isOn: $darkModeEnabled) // Added toggle Toggle("Dark Mode", isOn: $darkModeEnabled)
NavigationLink("Accent Color", destination: Text("Color Picker (Placeholder)")) NavigationLink("Accent Color", destination: Text("Color Picker (Placeholder)"))
} }
@ -175,11 +188,21 @@ struct SettingsView: View {
} }
NavigationLink("Privacy Policy", destination: Text("Privacy Policy Details (Placeholder)")) NavigationLink("Privacy Policy", destination: Text("Privacy Policy Details (Placeholder)"))
} }
Section("test") {
Text("Hello From Kotlin Multiplatform: \n\(Greeting().greet()) ") Section("Kotlin Multiplatform 测试") {
Text("来自KMP的问候:\n\(viewModel.greetingText)")
.padding(.vertical, 8)
Button("重新加载问候语") {
viewModel.loadGreeting()
}
.foregroundColor(.accentColor)
} }
} }
.navigationTitle("Settings") .navigationTitle("Settings")
.onAppear {
viewModel.loadGreeting()
}
} }
.navigationViewStyle(StackNavigationViewStyle()) .navigationViewStyle(StackNavigationViewStyle())
} }