feat: 优化线下收款日历交互与选中态

This commit is contained in:
2026-08-26 14:19:02 +08:00
parent 4351c26e74
commit 9fce6ef713
5 changed files with 262 additions and 58 deletions
@@ -24,6 +24,8 @@ final class OfflineCollectionDailyViewController: BaseViewController {
private let headerContainer = UIView()
private let headerStack = UIStackView()
private let calendarView = OfflineCollectionCalendarView()
private var calendarHeightConstraint: Constraint?
private var isAnimatingHeaderResize = false
private let summaryCard = UIView()
private let summaryDateLabel = UILabel()
private let totalValue = UILabel()
@@ -75,7 +77,7 @@ final class OfflineCollectionDailyViewController: BaseViewController {
guard let self else { return }
Task { await self.viewModel.selectBusinessDate(date) }
}
calendarView.onModeChanged = { [weak self] in self?.resizeHeader() }
calendarView.onModeChanged = { [weak self] animated in self?.resizeHeader(animated: animated) }
viewModel.onStateChange = { [weak self] in Task { @MainActor in self?.applyState() } }
}
@@ -91,7 +93,9 @@ final class OfflineCollectionDailyViewController: BaseViewController {
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
resizeHeader()
if !isAnimatingHeaderResize {
resizeHeader()
}
}
private func configureTableView() {
@@ -116,6 +120,9 @@ final class OfflineCollectionDailyViewController: BaseViewController {
make.leading.trailing.equalToSuperview().inset(16)
}
headerStack.addArrangedSubview(calendarView)
calendarView.snp.makeConstraints { make in
calendarHeightConstraint = make.height.equalTo(calendarView.preferredHeight).constraint
}
configureSummaryCard()
headerStack.addArrangedSubview(summaryCard)
@@ -351,9 +358,41 @@ final class OfflineCollectionDailyViewController: BaseViewController {
}
}
private func resizeHeader() {
private func resizeHeader(animated: Bool = false) {
guard tableView.bounds.width > 0 else { return }
if isAnimatingHeaderResize, !animated { return }
headerContainer.frame.size.width = tableView.bounds.width
let targetCalendarHeight = calendarView.preferredHeight
if animated,
!UIAccessibility.isReduceMotionEnabled,
headerContainer.frame.height > 0,
calendarView.bounds.height > 0 {
let targetHeaderHeight = headerContainer.frame.height + targetCalendarHeight - calendarView.bounds.height
guard abs(headerContainer.frame.height - targetHeaderHeight) > 0.5 else { return }
calendarHeightConstraint?.update(offset: targetCalendarHeight)
isAnimatingHeaderResize = true
UIView.animate(
withDuration: 0.3,
delay: 0,
options: [.curveEaseInOut, .beginFromCurrentState],
animations: { [weak self] in
guard let self else { return }
headerContainer.frame.size.height = targetHeaderHeight
headerContainer.layoutIfNeeded()
tableView.tableHeaderView = headerContainer
tableView.layoutIfNeeded()
},
completion: { [weak self] _ in
guard let self else { return }
isAnimatingHeaderResize = false
resizeHeader()
}
)
return
}
calendarHeightConstraint?.update(offset: targetCalendarHeight)
headerContainer.setNeedsLayout()
headerContainer.layoutIfNeeded()
let height = headerContainer.systemLayoutSizeFitting(
@@ -362,8 +401,13 @@ final class OfflineCollectionDailyViewController: BaseViewController {
verticalFittingPriority: .fittingSizeLevel
).height
guard abs(headerContainer.frame.height - height) > 0.5 else { return }
headerContainer.frame.size.height = height
tableView.tableHeaderView = headerContainer
let updates = { [weak self] in
guard let self else { return }
headerContainer.frame.size.height = height
tableView.tableHeaderView = headerContainer
tableView.layoutIfNeeded()
}
updates()
}
@MainActor private func presentFeedbackIfNeeded() {
@@ -371,15 +415,9 @@ final class OfflineCollectionDailyViewController: BaseViewController {
switch viewModel.settlementState {
case .idle, .processing:
return
case let .success(result):
feedbackPresented = true
let alert = UIAlertController(
title: "补缴成功",
message: "营业日:\(result.date)\n共结清 \(result.updatedCount) 笔",
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "完成", style: .default) { [weak self] _ in self?.finishFeedback() })
present(alert, animated: true)
case .success:
// 成功后页面数据已经刷新,直接清理反馈状态,不再阻断用户操作。
viewModel.clearSettlementFeedback()
case let .failed(message, canRetry):
feedbackPresented = true
let alert = UIAlertController(title: "补缴失败", message: message, preferredStyle: .alert)