Files
suixinkan_uikit/suixinkan/UI/Statistics/StatisticsDailyListView.swift
汉秋 87696a4774 Introduce unified design tokens and migrate Home, Orders, and Statistics UI.
Establish AppColor/AppFont/AppSpacing/AppRadius theming, shared components, and design-system docs so pilot screens align with Android while keeping iOS-native spacing and touch targets.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-06 17:58:18 +08:00

175 lines
5.9 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// 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?()
}
}
}