// // StatisticsViewController.swift // suixinkan // import SnapKit import UIKit /// 数据 Tab 根页面,展示汇总指标与日明细列表。 final class StatisticsViewController: BaseViewController { private let viewModel = StatisticsViewModel() private let statisticsAPI = NetworkServices.shared.statisticsAPI private let summaryCardView = StatisticsSummaryCardView() private let dailyListView = StatisticsDailyListView() private var hasInitialized = false private var isLoadingMore = false override func setupNavigationBar() { title = "数据" } override func setupUI() { view.backgroundColor = UIColor(hex: 0xF7FAFF) view.addSubview(summaryCardView) view.addSubview(dailyListView) } override func setupConstraints() { summaryCardView.snp.makeConstraints { make in make.top.equalTo(view.safeAreaLayoutGuide).offset(16) make.leading.trailing.equalToSuperview().inset(16) } dailyListView.snp.makeConstraints { make in make.top.equalTo(summaryCardView.snp.bottom).offset(16) make.leading.trailing.equalToSuperview().inset(16) make.bottom.equalTo(view.safeAreaLayoutGuide).inset(16) } } override func bindActions() { viewModel.onStateChange = { [weak self] in Task { @MainActor in self?.applyViewModel() } } viewModel.onShowMessage = { [weak self] message in Task { @MainActor in self?.showToast(message) } } summaryCardView.onPeriodSelected = { [weak self] period in self?.selectPeriod(period) } dailyListView.onRangeTap = { [weak self] in self?.presentDateRangeSheet() } dailyListView.onRefresh = { [weak self] in self?.refreshList() } dailyListView.onLoadMore = { [weak self] in self?.loadMoreIfNeeded() } } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) guard !hasInitialized else { return } hasInitialized = true Task { await initializeStatistics() } } private func applyViewModel() { summaryCardView.apply( selectedPeriod: viewModel.selectedPeriod, selectedPeriodTime: viewModel.selectedPeriodTime, summary: viewModel.statistics ) dailyListView.apply( rangeText: viewModel.statisticsRangeText, items: viewModel.statisticsList, isRefreshing: viewModel.isRefreshing, canLoadMore: viewModel.canLoadMore ) if viewModel.isLoadingSummary { showLoading() } else { hideLoading() } } private func initializeStatistics() async { showLoading() await viewModel.initStatistics(api: statisticsAPI) hideLoading() } private func selectPeriod(_ period: String) { Task { showLoading() await viewModel.selectPeriod(period, api: statisticsAPI) hideLoading() } } private func refreshList() { Task { await viewModel.refreshStatistics(api: statisticsAPI) } } private func loadMoreIfNeeded() { guard viewModel.canLoadMore, !isLoadingMore else { return } isLoadingMore = true Task { await viewModel.loadMore(api: statisticsAPI) isLoadingMore = false } } private func presentDateRangeSheet() { let controller = StatisticsDateRangeViewController( rangeStart: viewModel.rangeStartDate, rangeEnd: viewModel.rangeEndDate ) controller.onShortcutSelected = { [weak self, weak controller] shortcut in guard let self else { return } Task { await self.viewModel.updateRangeByShortcut(shortcut, api: self.statisticsAPI) controller?.dismiss(animated: true) } } controller.onRangeSelected = { [weak self, weak controller] start, end in guard let self else { return } Task { await self.viewModel.updateRangeByCalendar(start: start, end: end, api: self.statisticsAPI) controller?.dismiss(animated: true) } } if let sheet = controller.sheetPresentationController { sheet.detents = [.medium(), .large()] sheet.prefersGrabberVisible = true } present(controller, animated: true) } }