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 Shared
// MARK: - BottomBar
struct BottomBar: View {
// Use meaningful tab names
@State private var selectedTab: Tab = .today
// Define Tabs with associated icons and names
enum Tab: CaseIterable {
case today, weekly, settings
@ -28,69 +28,61 @@ struct BottomBar: View {
var body: some View {
TabView(selection: $selectedTab) {
// Use ContentView for the "Today" tab
ContentView()
.tag(Tab.today)
.tabItem { Label(Tab.today.title, systemImage: Tab.today.iconName) }
// Placeholder for Weekly view
WeeklyView()
.tag(Tab.weekly)
.tabItem { Label(Tab.weekly.title, systemImage: Tab.weekly.iconName) }
// Placeholder for Settings view
SettingsView()
.tag(Tab.settings)
.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 {
// 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 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 {
(weeklyData.max() ?? goalHours) * 1.1 // Add 10% buffer
(weeklyData.max() ?? goalHours) * 1.1
}
var body: some View {
NavigationView {
// Use List for standard iOS layout
List {
Section("Usage Trend") {
// Bar Chart Visualization
HStack(alignment: .bottom, spacing: 15) { // Increased spacing
HStack(alignment: .bottom, spacing: 15) {
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]))
.font(.caption)
.foregroundColor(.accentColor)
.lineLimit(1)
.rotationEffect(.degrees(-90)) // Rotate text for better fit if needed
.offset(y: -25) // Adjust offset if rotated
.frame(height: 50) // Give space for text
.rotationEffect(.degrees(-90))
.offset(y: -25)
.frame(height: 50)
Rectangle()
.fill(weeklyData[index] > goalHours ? Color.orange : Color.accentColor) // Highlight days over goal
// Scale height relative to the max value
.frame(height: max(1, CGFloat(weeklyData[index] / maxValue) * 150)) // Ensure min height, scale based on max
.cornerRadius(5) // Rounded corners for bars
.fill(weeklyData[index] > goalHours ? Color.orange : Color.accentColor)
.frame(height: max(1, CGFloat(weeklyData[index] / maxValue) * 150))
.cornerRadius(5)
Text(days[index])
.font(.caption)
.foregroundColor(.secondary)
}
.frame(maxWidth: .infinity) // Allow bars to take equal width
.frame(maxWidth: .infinity)
}
}
.frame(height: 220) // Adjust overall chart height
.padding(.vertical) // Add padding around the chart
.frame(height: 220)
.padding(.vertical)
}
Section("Summary") {
@ -114,33 +106,54 @@ struct WeeklyView: View {
}
}
}
.listStyle(.insetGrouped) // Use inset grouped style
.listStyle(.insetGrouped)
.navigationTitle("Weekly Stats")
}
.navigationViewStyle(StackNavigationViewStyle())
}
// Helper function to calculate weekly average
func calculateWeeklyAverage() -> String {
let totalHours = weeklyData.reduce(0, +)
let averageHours = totalHours / Double(weeklyData.count)
let formatter = DateComponentsFormatter()
formatter.allowedUnits = [.hour, .minute]
formatter.unitsStyle = .abbreviated
// Convert hours to seconds for formatter
return formatter.string(from: TimeInterval(averageHours * 3600)) ?? "0m"
}
// Helper property for days over goal
var daysOverGoalCount: Int {
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 {
@StateObject private var viewModel = GreetingViewModel()
@State private var usageReminders = true
@State private var darkModeEnabled = false // Example state
@State private var darkModeEnabled = false
@State private var selectedLimit = "No Limit"
let appLimits = ["No Limit", "1 hour", "2 hours", "Custom"]
@ -149,7 +162,7 @@ struct SettingsView: View {
Form {
Section("Notifications") {
Toggle("Usage Reminders", isOn: $usageReminders)
Toggle("Goal Achievement Alerts", isOn: .constant(false)) // Added toggle
Toggle("Goal Achievement Alerts", isOn: .constant(false))
}
Section("Usage Limits") {
@ -158,11 +171,11 @@ struct SettingsView: View {
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") {
Toggle("Dark Mode", isOn: $darkModeEnabled) // Added toggle
Toggle("Dark Mode", isOn: $darkModeEnabled)
NavigationLink("Accent Color", destination: Text("Color Picker (Placeholder)"))
}
@ -175,11 +188,21 @@ struct SettingsView: View {
}
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")
.onAppear {
viewModel.loadGreeting()
}
}
.navigationViewStyle(StackNavigationViewStyle())
}