// // StatisticsDailyListView.swift // suixinkan // import SnapKit import UIKit /// 数据统计日明细列表区域,含时间范围选择与分页列表。 final class StatisticsDailyListView: UIView { var onRangeTap: (() -> Void)? var onRefresh: (() -> Void)? var onLoadMore: (() -> Void)? private let titleLabel = UILabel() private let rangeButton = UIControl() private let rangeLabel = UILabel() private let arrowView = UIImageView() private let divider = UIView() private let tableView = UITableView(frame: .zero, style: .plain) private let refreshControl = UIRefreshControl() private let footerIndicator = UIActivityIndicatorView(style: .medium) private var items: [StatisticsDailyItem] = [] private var showsLoadMoreFooter = false override init(frame: CGRect) { super.init(frame: frame) setupUI() } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func apply(rangeText: String, items: [StatisticsDailyItem], isRefreshing: Bool, canLoadMore: Bool) { rangeLabel.text = rangeText self.items = items showsLoadMoreFooter = canLoadMore tableView.reloadData() updateFooterVisibility() if !isRefreshing { refreshControl.endRefreshing() } } private func setupUI() { backgroundColor = AppColor.cardBackground layer.cornerRadius = AppRadius.lg clipsToBounds = true titleLabel.text = "时间范围" titleLabel.font = .app(.bodyMedium) titleLabel.textColor = AppColor.textPrimary rangeButton.backgroundColor = AppColor.inputBackground rangeButton.layer.cornerRadius = AppRadius.xs rangeButton.addTarget(self, action: #selector(rangeTapped), for: .touchUpInside) rangeLabel.font = .app(.body) rangeLabel.textColor = AppColor.textSecondary rangeLabel.text = "今日" arrowView.image = UIImage(systemName: "chevron.down") arrowView.tintColor = AppColor.textSecondary arrowView.contentMode = .scaleAspectFit divider.backgroundColor = AppColor.inputBackground tableView.backgroundColor = .clear tableView.separatorStyle = .none tableView.dataSource = self tableView.delegate = self tableView.register(StatisticsDailyCell.self, forCellReuseIdentifier: StatisticsDailyCell.reuseIdentifier) tableView.refreshControl = refreshControl refreshControl.addTarget(self, action: #selector(refreshPulled), for: .valueChanged) footerIndicator.hidesWhenStopped = true addSubview(titleLabel) addSubview(rangeButton) rangeButton.addSubview(rangeLabel) rangeButton.addSubview(arrowView) addSubview(divider) addSubview(tableView) titleLabel.snp.makeConstraints { make in make.top.leading.equalToSuperview().inset(16) } rangeButton.snp.makeConstraints { make in make.centerY.equalTo(titleLabel) make.trailing.equalToSuperview().inset(16) make.height.equalTo(36) make.width.greaterThanOrEqualTo(166) } rangeLabel.snp.makeConstraints { make in make.leading.equalToSuperview().inset(12) make.centerY.equalToSuperview() make.trailing.lessThanOrEqualTo(arrowView.snp.leading).offset(-8) } arrowView.snp.makeConstraints { make in make.trailing.equalToSuperview().inset(12) make.centerY.equalToSuperview() make.size.equalTo(16) } divider.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(12) make.leading.trailing.equalToSuperview().inset(16) make.height.equalTo(1) } tableView.snp.makeConstraints { make in make.top.equalTo(divider.snp.bottom).offset(12) make.leading.trailing.bottom.equalToSuperview().inset(16) } } @objc private func rangeTapped() { onRangeTap?() } @objc private func refreshPulled() { onRefresh?() } private func updateFooterVisibility() { if showsLoadMoreFooter { footerIndicator.startAnimating() tableView.tableFooterView = footerContainer() } else { footerIndicator.stopAnimating() tableView.tableFooterView = UIView(frame: .zero) } } private func footerContainer() -> UIView { let container = UIView(frame: CGRect(x: 0, y: 0, width: bounds.width, height: 56)) container.addSubview(footerIndicator) footerIndicator.center = CGPoint(x: container.bounds.midX, y: container.bounds.midY) footerIndicator.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin] return container } } extension StatisticsDailyListView: UITableViewDataSource, UITableViewDelegate { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { items.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell( withIdentifier: StatisticsDailyCell.reuseIdentifier, for: indexPath ) as? StatisticsDailyCell else { return UITableViewCell() } cell.configure(with: items[indexPath.row], showsDivider: indexPath.row < items.count - 1) return cell } func scrollViewDidScroll(_ scrollView: UIScrollView) { guard showsLoadMoreFooter else { return } let offsetY = scrollView.contentOffset.y let contentHeight = scrollView.contentSize.height let frameHeight = scrollView.frame.size.height guard contentHeight > frameHeight else { return } if offsetY > contentHeight - frameHeight - 120 { onLoadMore?() } } }