// // StatisticsViewModel.swift // suixinkan // // Created by Codex on 2026/6/22. // import Foundation @MainActor /// 数据统计页面 ViewModel,管理时间段、汇总数据、每日明细和分页状态。 final class StatisticsViewModel { var onChange: (() -> Void)? var selectedPeriod: StatisticsPeriod = .today { didSet { onChange?() } } private(set) var loading = false { didSet { onChange?() } } private(set) var loadingMore = false { didSet { onChange?() } } private(set) var summary = StatisticsSummaryResponse() { didSet { onChange?() } } private(set) var dailyItems: [StatisticsDailyItem] = [] { didSet { onChange?() } } private(set) var totalDailyCount = 0 { didSet { onChange?() } } private(set) var lastRefreshAt: Date? { didSet { onChange?() } } private var page = 1 private let pageSize = 15 /// 当前日数据是否还有下一页。 var hasMore: Bool { dailyItems.count < totalDailyCount } /// 切换统计时间段,并重置分页重新加载。 func selectPeriod(_ period: StatisticsPeriod, api: StatisticsServing, scenicId: Int?, roleId: Int?) async throws { selectedPeriod = period try await reload(api: api, scenicId: scenicId, roleId: roleId) } /// 刷新统计汇总和第一页日数据,缺少景区时清空旧状态。 func reload(api: StatisticsServing, scenicId: Int?, roleId: Int?, showLoading: Bool = true) async throws { guard let scenicId else { reset() return } if showLoading { loading = true } defer { if showLoading { loading = false } } let isScenicAdmin = roleId == 53 async let summaryResult = api.summary( scenicId: scenicId, range: selectedPeriod.apiRange, isScenicAdmin: isScenicAdmin ) async let dailyResult = api.dailyList( scenicId: scenicId, startTime: selectedPeriod.apiStartTime, endTime: selectedPeriod.apiEndTime, page: 1, pageSize: pageSize, isScenicAdmin: isScenicAdmin ) let (summaryData, dailyData) = try await (summaryResult, dailyResult) summary = summaryData dailyItems = dailyData.data totalDailyCount = dailyData.total page = 1 lastRefreshAt = Date() } /// 加载每日统计下一页。 func loadMore(api: StatisticsServing, scenicId: Int?, roleId: Int?) async throws { guard !loadingMore, hasMore, let scenicId else { return } loadingMore = true defer { loadingMore = false } let nextPage = page + 1 let result = try await api.dailyList( scenicId: scenicId, startTime: selectedPeriod.apiStartTime, endTime: selectedPeriod.apiEndTime, page: nextPage, pageSize: pageSize, isScenicAdmin: roleId == 53 ) dailyItems.append(contentsOf: result.data) totalDailyCount = result.total page = nextPage } /// 清空统计状态。 private func reset() { summary = StatisticsSummaryResponse() dailyItems = [] totalDailyCount = 0 page = 1 lastRefreshAt = nil loading = false loadingMore = false } }