// // InviteViewControllers.swift // suixinkan // // Created by Codex on 2026/6/26. // import SnapKit import UIKit /// 摄影师邀请页。 final class PhotographerInviteViewController: UIViewController { private let services = AppServices.shared private let viewModel = PhotographerInviteViewModel() private let scrollView = UIScrollView() private let contentStack = UIStackView() private let codeLabel = UILabel() private let urlLabel = UILabel() private let qrImageView = UIImageView() private let rulesLabel = UILabel() private let activityIndicator = UIActivityIndicatorView(style: .medium) override func viewDidLoad() { super.viewDidLoad() title = "邀请摄影师" view.backgroundColor = UIColor(hex: 0xF5F7FA) setupUI() viewModel.onChange = { [weak self] in self?.render() } Task { await viewModel.reload(api: services.inviteAPI) } } private func setupUI() { navigationItem.rightBarButtonItems = [ UIBarButtonItem(title: "复制码", style: .plain, target: self, action: #selector(copyCode)), UIBarButtonItem(title: "复制链接", style: .plain, target: self, action: #selector(copyURL)) ] contentStack.axis = .vertical contentStack.spacing = 12 scrollView.addSubview(contentStack) view.addSubview(scrollView) view.addSubview(activityIndicator) scrollView.snp.makeConstraints { $0.edges.equalToSuperview() } contentStack.snp.makeConstraints { make in make.edges.equalToSuperview().inset(16) make.width.equalTo(scrollView.snp.width).offset(-32) } activityIndicator.snp.makeConstraints { $0.center.equalToSuperview() } codeLabel.font = .systemFont(ofSize: 22, weight: .bold) urlLabel.font = .systemFont(ofSize: 14) urlLabel.numberOfLines = 0 urlLabel.textColor = AppDesign.textSecondary qrImageView.contentMode = .scaleAspectFit qrImageView.snp.makeConstraints { $0.height.equalTo(220) } rulesLabel.numberOfLines = 0 rulesLabel.font = .systemFont(ofSize: 14) rulesLabel.textColor = AppDesign.textSecondary contentStack.addArrangedSubview(codeLabel) contentStack.addArrangedSubview(urlLabel) contentStack.addArrangedSubview(qrImageView) contentStack.addArrangedSubview(rulesLabel) } private func render() { activityIndicator.isHidden = !viewModel.loading if viewModel.loading { activityIndicator.startAnimating() } else { activityIndicator.stopAnimating() } codeLabel.text = viewModel.inviteCode.isEmpty ? "邀请码加载中..." : "邀请码:\(viewModel.inviteCode)" urlLabel.text = viewModel.inviteUrl qrImageView.image = viewModel.qrImage rulesLabel.text = viewModel.rules.joined(separator: "\n") if let error = viewModel.errorMessage { rulesLabel.text = (rulesLabel.text ?? "") + "\n\n" + error } } @objc private func copyCode() { viewModel.copyInviteCode() } @objc private func copyURL() { viewModel.copyInviteUrl() } } extension PhotographerInviteViewModel: ViewModelBindable {} /// 邀请记录页。 final class InviteRecordViewController: ModuleTableViewController { private let viewModel = InviteRecordViewModel() private let summaryLabel = UILabel() override func viewDidLoad() { title = "邀请记录" navigationItem.rightBarButtonItem = UIBarButtonItem( title: "奖励明细", style: .plain, target: self, action: #selector(toggleTab) ) super.viewDidLoad() setupHeader() viewModel.onChange = { [weak self] in self?.updateSummary() self?.reloadTable() } } private func setupHeader() { summaryLabel.font = .systemFont(ofSize: 14) summaryLabel.textColor = AppDesign.textSecondary summaryLabel.numberOfLines = 0 summaryLabel.textAlignment = .center summaryLabel.frame = CGRect(x: 0, y: 0, width: view.bounds.width, height: 56) tableView.tableHeaderView = summaryLabel } override func tableRowCount() -> Int { viewModel.displayRows.count } override func configureCell(_ cell: TitleSubtitleTableViewCell, at indexPath: IndexPath) { let row = viewModel.displayRows[indexPath.row] cell.configure( title: row.title, subtitle: row.subtitle, detail: row.amount.isEmpty ? row.time : "\(row.amount) · \(row.time)" ) } override func reloadContent() async { await viewModel.reload( inviteAPI: services.inviteAPI, walletAPI: services.walletAPI, refresh: true ) updateSummary() } private func updateSummary() { summaryLabel.text = "累计奖励 \(viewModel.totalRewardText) · 可提现 \(viewModel.withdrawableText)" navigationItem.rightBarButtonItem?.title = viewModel.tab == .invite ? "奖励明细" : "邀请用户" } @objc private func toggleTab() { Task { let next: InviteRecordTab = viewModel.tab == .invite ? .reward : .invite await viewModel.selectTab(next, inviteAPI: services.inviteAPI, walletAPI: services.walletAPI) } } }