|
| 1 | +// |
| 2 | +// DashboardView.swift |
| 3 | +// MyTesla |
| 4 | +// |
| 5 | + |
| 6 | +import SwiftUI |
| 7 | +import SwiftData |
| 8 | + |
| 9 | +struct DashboardView: View { |
| 10 | + @StateObject private var viewModel = DashboardViewModel() |
| 11 | + @Environment(\.modelContext) private var modelContext |
| 12 | + @State private var vehicleId: Int? |
| 13 | + |
| 14 | + var body: some View { |
| 15 | + NavigationStack { |
| 16 | + ScrollView { |
| 17 | + VStack(spacing: 12) { |
| 18 | + if viewModel.isLoading && viewModel.status == nil { |
| 19 | + LoadingView() |
| 20 | + } else if let error = viewModel.errorMessage { |
| 21 | + ErrorView(message: error) { |
| 22 | + Task { await viewModel.refresh() } |
| 23 | + } |
| 24 | + } else { |
| 25 | + ForEach(viewModel.cardOrder, id: \.self) { cardType in |
| 26 | + switch cardType { |
| 27 | + case .vehicleInfo: |
| 28 | + if let vehicle = viewModel.vehicleDetail { |
| 29 | + VehicleInfoCard(vehicle: vehicle) |
| 30 | + } |
| 31 | + case .battery: |
| 32 | + if let status = viewModel.status { |
| 33 | + BatteryCard(status: status) |
| 34 | + } |
| 35 | + case .charging: |
| 36 | + if let status = viewModel.status, status.isCharging { |
| 37 | + ChargingCard(status: status) |
| 38 | + } |
| 39 | + case .drivingSummary: |
| 40 | + if let drive = viewModel.recentDrive { |
| 41 | + DrivingSummaryCard(drive: drive) |
| 42 | + } |
| 43 | + case .statusGrid: |
| 44 | + if let status = viewModel.status { |
| 45 | + StatusGridCard(status: status) |
| 46 | + } |
| 47 | + case .location: |
| 48 | + if let status = viewModel.status { |
| 49 | + LocationCard(status: status) |
| 50 | + } |
| 51 | + } |
| 52 | + } |
| 53 | + } |
| 54 | + } |
| 55 | + .padding(.horizontal, 16) |
| 56 | + .padding(.top, 8) |
| 57 | + } |
| 58 | + .background(Color(.systemBackground)) |
| 59 | + .refreshable { |
| 60 | + await viewModel.refresh() |
| 61 | + } |
| 62 | + .navigationTitle("MyTesla") |
| 63 | + .navigationBarTitleDisplayMode(.large) |
| 64 | + .toolbar { |
| 65 | + ToolbarItem(placement: .topBarTrailing) { |
| 66 | + if let lastUpdated = viewModel.lastUpdated { |
| 67 | + Text(lastUpdated.timeAgoDisplay()) |
| 68 | + .font(.caption) |
| 69 | + .foregroundColor(.secondary) |
| 70 | + } |
| 71 | + } |
| 72 | + } |
| 73 | + .task { |
| 74 | + await loadVehicleAndRefresh() |
| 75 | + } |
| 76 | + } |
| 77 | + } |
| 78 | + |
| 79 | + private func loadVehicleAndRefresh() async { |
| 80 | + do { |
| 81 | + let vehicles = try await APIClient.shared.getVehicles() |
| 82 | + if let first = vehicles.first { |
| 83 | + vehicleId = first.id |
| 84 | + viewModel.configure(context: modelContext, vehicleId: first.id) |
| 85 | + await viewModel.refresh() |
| 86 | + } else { |
| 87 | + viewModel.errorMessage = "未找到车辆,请检查 TeslaMate 配置" |
| 88 | + } |
| 89 | + } catch { |
| 90 | + viewModel.errorMessage = error.localizedDescription |
| 91 | + } |
| 92 | + } |
| 93 | +} |
0 commit comments