// // PaymentCollectionViewController.swift // suixinkan // // Created by Codex on 2026/6/26. // import SnapKit import UIKit /// 收款页。 final class PaymentCollectionViewController: UIViewController { private let services = AppServices.shared private let viewModel = PaymentCollectionViewModel() private let qrImageView = UIImageView() private let statusLabel = UILabel() private let amountField = UITextField() private let remarkField = UITextField() private let activityIndicator = UIActivityIndicatorView(style: .large) /// 视图加载完成后的 UI 初始化与数据绑定。 override func viewDidLoad() { super.viewDidLoad() title = "收款" view.backgroundColor = UIColor(hex: 0xF5F7FA) setupUI() viewModel.onChange = { [weak self] in self?.render() } Task { await viewModel.loadPayCode(api: services.paymentAPI, scenicId: services.currentScenicId) } } /// 初始化UI相关 UI 或状态。 private func setupUI() { qrImageView.contentMode = .scaleAspectFit statusLabel.numberOfLines = 0 statusLabel.textAlignment = .center statusLabel.textColor = AppDesign.textSecondary amountField.placeholder = "动态金额" amountField.borderStyle = .roundedRect amountField.keyboardType = .decimalPad remarkField.placeholder = "备注" remarkField.borderStyle = .roundedRect let stack = UIStackView(arrangedSubviews: [qrImageView, statusLabel, amountField, remarkField]) stack.axis = .vertical stack.spacing = 12 view.addSubview(stack) view.addSubview(activityIndicator) stack.snp.makeConstraints { make in make.top.equalTo(view.safeAreaLayoutGuide).offset(24) make.leading.trailing.equalToSuperview().inset(24) } qrImageView.snp.makeConstraints { $0.height.equalTo(240) } activityIndicator.snp.makeConstraints { $0.center.equalTo(qrImageView) } navigationItem.rightBarButtonItems = [ UIBarButtonItem(title: "生成", style: .plain, target: self, action: #selector(applyAmount)), UIBarButtonItem(title: "轮询", style: .plain, target: self, action: #selector(togglePolling)) ] } /// render 业务逻辑。 private func render() { qrImageView.image = viewModel.qrImage statusLabel.text = viewModel.errorMessage ?? String(describing: viewModel.status) if viewModel.isLoading { activityIndicator.startAnimating() } else { activityIndicator.stopAnimating() } } /// apply金额相关逻辑。 @objc private func applyAmount() { viewModel.amountText = amountField.text ?? "" viewModel.remarkText = remarkField.text ?? "" _ = viewModel.applyDynamicAmount() } /// togglePolling相关逻辑。 @objc private func togglePolling() { Task { await viewModel.pollUntilPaymentDetected( api: services.paymentAPI, scenicId: services.currentScenicId ) } } } extension PaymentCollectionViewModel: ViewModelBindable {}