// // ScenicSelectionViewModel.swift // suixinkan // import Foundation /// 权限申请审核中状态,对齐 Android role-apply pending status。 enum PermissionApplyPendingStatus { static let reviewing = 1 static let rejected = 3 } /// 景区选择 ViewModel,对齐 Android `ScenicSelectionViewModel`。 final class ScenicSelectionViewModel { private(set) var spots: [ScenicSpotDisplayItem] = [] private(set) var searchQuery = "" private(set) var currentLocationText = "最近的景区" private(set) var isLoading = false var onStateChange: (() -> Void)? var onShowMessage: ((String) -> Void)? var onSelectionComplete: (() -> Void)? var onNavigateApply: (() -> Void)? var onNavigateApplyStatus: ((String) -> Void)? private let appStore: AppStore private let locationProvider: HomeLocationProvider private var currentLat: Double? private var currentLng: Double? init(appStore: AppStore = .shared, locationProvider: HomeLocationProvider = HomeLocationProvider()) { self.appStore = appStore self.locationProvider = locationProvider } func loadFromLocal() { let scenicList = resolvedScenicList() spots = ScenicSpotDisplayMapper.buildItems( scenicList: scenicList, searchQuery: searchQuery, currentLat: currentLat, currentLng: currentLng, selectedScenicId: appStore.currentScenicId ) notifyStateChange() } func onSearchQueryChange(_ query: String) { searchQuery = query loadFromLocal() } func startLocation() async { isLoading = true notifyStateChange() do { let snapshot = try await locationProvider.requestSnapshot() currentLat = snapshot.latitude currentLng = snapshot.longitude let address = snapshot.address.trimmingCharacters(in: .whitespacesAndNewlines) currentLocationText = address.isEmpty ? "最近的景区" : address loadFromLocal() } catch HomeLocationProviderError.permissionDenied { onShowMessage?("未授予定位权限,无法获取附近景区") } catch { onShowMessage?("定位失败: \(error.localizedDescription)") } isLoading = false notifyStateChange() } func selectSpot(_ spot: ScenicSpotDisplayItem) { appStore.currentScenicId = spot.id appStore.currentScenicName = spot.name NotificationCenter.default.post( name: NotificationName.scenicDidChange, object: nil, userInfo: [ NotificationUserInfoKey.scenicId: spot.id, NotificationUserInfoKey.scenicName: spot.name, ] ) loadFromLocal() onSelectionComplete?() } func onApplyNow(api: HomeAPI) async { isLoading = true notifyStateChange() defer { isLoading = false notifyStateChange() } do { let pendingList = try await api.roleApplyPending() if let first = pendingList.first, first.status == PermissionApplyPendingStatus.reviewing || first.status == PermissionApplyPendingStatus.rejected { onNavigateApplyStatus?(first.code) } else { onNavigateApply?() } } catch { onNavigateApply?() } } private func resolvedScenicList() -> [ScenicInfo] { let cached = appStore.roleScenicList() if !cached.isEmpty { return cached } let rolePermissionList = appStore.rolePermissionList() guard !rolePermissionList.isEmpty else { return [] } return appStore.matchRolePermissionItem(in: rolePermissionList)?.scenic ?? [] } private func notifyStateChange() { onStateChange?() } }