import Foundation @MainActor final class FleetViewModel: ObservableObject { struct Row: Equatable, Identifiable { let id: UUID var instance: Instance var status: ConnectionStatus var testing: Bool } @Published var rows: [Row] = [] @Published var searchText = "" @Published var selectedIDs: Set = [] @Published var selectionMode = false @Published var isLoading = false @Published var errorMessage: String? @Published var showingForm = false private let repository: any InstanceRepository private let probe: ConnectionProbe private let credentials: any CredentialStore init( repository: any InstanceRepository, probe: ConnectionProbe, credentials: any CredentialStore ) { self.repository = repository self.probe = probe self.credentials = credentials } func refresh() async { isLoading = true errorMessage = nil do { let query = InstanceQuery(search: searchText.isEmpty ? nil : searchText) let instances = try await repository.list(query) rows = instances.map { Row(id: $0.id, instance: $0, status: .unknown, testing: true) } await withTaskGroup(of: Void.self) { group in for instance in instances { group.addTask { [probe] in let result = try? await probe.test(instance) await self.applyProbe(instance.id, result) } } } } catch { errorMessage = "无法刷新节点列表" } isLoading = false } func add(_ input: InstanceInput) async throws { let created = try await repository.create(input) if let password = input.password, !password.isEmpty { try credentials.set(instanceID: created.id, password: password) } showingForm = false await refresh() } func delete(_ ids: [UUID]) async { for id in ids { if let row = rows.first(where: { $0.id == id }) { do { try await repository.delete(id, expectedRevision: row.instance.configRevision) try credentials.remove(instanceID: id) } catch { errorMessage = "删除 \(row.instance.name) 失败" } } } selectedIDs.removeAll() selectionMode = false await refresh() } private func applyProbe(_ id: UUID, _ result: ConnectionResult?) { guard let index = rows.firstIndex(where: { $0.id == id }) else { return } rows[index].status = result?.status ?? .unknown rows[index].testing = false } }