// // OfflineCollectionHomeView.swift // suixinkan // import SnapKit import UIKit /// 收款首页中的线下收款入口、今日汇总和历史逾期提醒区域。 final class OfflineCollectionHomeView: UIView { var onRegister: (() -> Void)? var onOpenToday: (() -> Void)? var onOpenOverdue: (() -> Void)? private let contentStack = UIStackView() private let sectionTitleLabel = UILabel() private let overdueControl = UIControl() private let overdueTitleLabel = UILabel() private let overdueDetailLabel = UILabel() private let overdueActionLabel = UILabel() private let registerControl = UIControl() private let registerIconView = UIImageView() private let registerTitleLabel = UILabel() private let registerSubtitleLabel = UILabel() private let registerActionLabel = UILabel() private let summaryControl = UIControl() private let summaryTitleLabel = UILabel() private let summaryAmountLabel = UILabel() private let summaryCountLabel = UILabel() private let summaryRegisteredLabel = UILabel() private let summaryStatusLabel = UILabel() private let summaryActionLabel = UILabel() override init(frame: CGRect) { super.init(frame: frame) setupUI() setupConstraints() bindActions() } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } /// 根据今日与历史逾期汇总刷新页面内容。 func apply( today: OfflineDailySummary, overdueDayCount: Int, overdueRecordCount: Int, overdueAmountFen: Int ) { let hasOverdue = overdueDayCount > 0 && overdueAmountFen > 0 overdueControl.isHidden = !hasOverdue overdueDetailLabel.text = "\(overdueDayCount) 个营业日,共 \(overdueRecordCount) 笔,待补缴 \(OfflineCollectionMoney.display(overdueAmountFen))" summaryAmountLabel.text = OfflineCollectionMoney.display(today.pendingAmountFen) summaryCountLabel.text = "\(today.pendingCount) 笔线下收款" summaryRegisteredLabel.text = "今日已登记 \(OfflineCollectionMoney.display(today.totalAmountFen))" let isSettled = today.totalCount > 0 && today.pendingAmountFen == 0 summaryStatusLabel.text = isSettled ? "今日已结清" : "" summaryStatusLabel.isHidden = !isSettled summaryActionLabel.text = today.pendingAmountFen > 0 ? "去补缴" : "查看明细" summaryActionLabel.backgroundColor = today.pendingAmountFen > 0 ? AppColor.primary : UIColor(hex: 0xEEF2F7) summaryActionLabel.textColor = today.pendingAmountFen > 0 ? .white : AppColor.textSecondary } private func setupUI() { backgroundColor = .clear contentStack.axis = .vertical contentStack.spacing = AppSpacing.md sectionTitleLabel.text = "线下收款" sectionTitleLabel.font = .systemFont(ofSize: 16, weight: .semibold) sectionTitleLabel.textColor = AppColor.textPrimary configureCard(overdueControl, backgroundColor: UIColor(hex: 0xFFF1F0)) overdueControl.layer.borderColor = UIColor(hex: 0xFFCCC7).cgColor overdueControl.layer.borderWidth = 1 overdueTitleLabel.text = "存在逾期未补缴" overdueTitleLabel.font = .systemFont(ofSize: 15, weight: .semibold) overdueTitleLabel.textColor = AppColor.danger overdueDetailLabel.font = .systemFont(ofSize: 13) overdueDetailLabel.textColor = UIColor(hex: 0x8C2F27) overdueDetailLabel.numberOfLines = 0 configureActionLabel(overdueActionLabel, title: "立即处理", background: AppColor.danger, foreground: .white) configureCard(registerControl, backgroundColor: .white) registerIconView.image = UIImage(systemName: "plus.circle.fill") registerIconView.tintColor = AppColor.primary registerIconView.contentMode = .scaleAspectFit registerTitleLabel.text = "线下收款登记" registerTitleLabel.font = .systemFont(ofSize: 16, weight: .semibold) registerTitleLabel.textColor = AppColor.textPrimary registerSubtitleLabel.text = "线下收款后,请及时登记并在当日完成补缴" registerSubtitleLabel.font = .systemFont(ofSize: 13) registerSubtitleLabel.textColor = AppColor.textSecondary registerSubtitleLabel.numberOfLines = 0 configureActionLabel(registerActionLabel, title: "去登记", background: UIColor(hex: 0xEEF5FF), foreground: AppColor.primary) configureCard(summaryControl, backgroundColor: .white) summaryTitleLabel.text = "今日待补缴" summaryTitleLabel.font = .systemFont(ofSize: 15, weight: .semibold) summaryTitleLabel.textColor = AppColor.textPrimary summaryAmountLabel.font = .systemFont(ofSize: 30, weight: .bold) summaryAmountLabel.textColor = AppColor.textPrimary summaryCountLabel.font = .systemFont(ofSize: 13) summaryCountLabel.textColor = UIColor(hex: 0xD97706) summaryRegisteredLabel.font = .systemFont(ofSize: 13) summaryRegisteredLabel.textColor = AppColor.textSecondary summaryStatusLabel.font = .systemFont(ofSize: 13, weight: .semibold) summaryStatusLabel.textColor = AppColor.success configureActionLabel(summaryActionLabel, title: "查看明细", background: UIColor(hex: 0xEEF2F7), foreground: AppColor.textSecondary) addSubview(contentStack) [sectionTitleLabel, overdueControl, registerControl, summaryControl].forEach(contentStack.addArrangedSubview) let overdueTextStack = UIStackView(arrangedSubviews: [overdueTitleLabel, overdueDetailLabel]) overdueTextStack.axis = .vertical overdueTextStack.spacing = 5 overdueControl.addSubview(overdueTextStack) overdueControl.addSubview(overdueActionLabel) let registerTextStack = UIStackView(arrangedSubviews: [registerTitleLabel, registerSubtitleLabel]) registerTextStack.axis = .vertical registerTextStack.spacing = 5 registerControl.addSubview(registerIconView) registerControl.addSubview(registerTextStack) registerControl.addSubview(registerActionLabel) let summaryTextStack = UIStackView(arrangedSubviews: [ summaryTitleLabel, summaryAmountLabel, summaryCountLabel, summaryRegisteredLabel, summaryStatusLabel, ]) summaryTextStack.axis = .vertical summaryTextStack.spacing = 6 summaryControl.addSubview(summaryTextStack) summaryControl.addSubview(summaryActionLabel) } private func setupConstraints() { contentStack.snp.makeConstraints { make in make.edges.equalToSuperview() } sectionTitleLabel.snp.makeConstraints { make in make.height.equalTo(24) } overdueControl.snp.makeConstraints { make in make.height.greaterThanOrEqualTo(84) } overdueControl.subviews.first?.snp.makeConstraints { make in make.leading.top.bottom.equalToSuperview().inset(AppSpacing.md) make.trailing.lessThanOrEqualTo(overdueActionLabel.snp.leading).offset(-AppSpacing.sm) } overdueActionLabel.snp.makeConstraints { make in make.trailing.equalToSuperview().inset(AppSpacing.md) make.centerY.equalToSuperview() make.height.equalTo(32) } registerControl.snp.makeConstraints { make in make.height.greaterThanOrEqualTo(96) } registerIconView.snp.makeConstraints { make in make.leading.equalToSuperview().inset(AppSpacing.md) make.centerY.equalToSuperview() make.width.height.equalTo(34) } registerTitleLabel.superview?.snp.makeConstraints { make in make.leading.equalTo(registerIconView.snp.trailing).offset(AppSpacing.sm) make.centerY.equalToSuperview() make.trailing.lessThanOrEqualTo(registerActionLabel.snp.leading).offset(-AppSpacing.sm) } registerActionLabel.snp.makeConstraints { make in make.trailing.equalToSuperview().inset(AppSpacing.md) make.centerY.equalToSuperview() make.height.equalTo(32) } summaryControl.snp.makeConstraints { make in make.height.greaterThanOrEqualTo(180) } summaryTitleLabel.superview?.snp.makeConstraints { make in make.leading.top.bottom.equalToSuperview().inset(AppSpacing.md) make.trailing.lessThanOrEqualTo(summaryActionLabel.snp.leading).offset(-AppSpacing.sm) } summaryActionLabel.snp.makeConstraints { make in make.trailing.equalToSuperview().inset(AppSpacing.md) make.bottom.equalToSuperview().inset(AppSpacing.md) make.height.equalTo(34) } } private func bindActions() { overdueControl.addTarget(self, action: #selector(overdueTapped), for: .touchUpInside) registerControl.addTarget(self, action: #selector(registerTapped), for: .touchUpInside) summaryControl.addTarget(self, action: #selector(summaryTapped), for: .touchUpInside) } private func configureCard(_ control: UIControl, backgroundColor: UIColor) { control.backgroundColor = backgroundColor control.layer.cornerRadius = AppRadius.lg control.clipsToBounds = true } private func configureActionLabel( _ label: UILabel, title: String, background: UIColor, foreground: UIColor ) { label.text = " \(title) " label.font = .systemFont(ofSize: 13, weight: .semibold) label.textAlignment = .center label.textColor = foreground label.backgroundColor = background label.layer.cornerRadius = 16 label.clipsToBounds = true label.setContentCompressionResistancePriority(.required, for: .horizontal) } @objc private func overdueTapped() { onOpenOverdue?() } @objc private func registerTapped() { onRegister?() } @objc private func summaryTapped() { onOpenToday?() } }