Files
suixinkan_ios_uikit/suixinkan_ios/Features/Invite/ViewControllers/InviteViewControllers.swift
汉秋 d99a5b1bf8 Advance UIKit rewrite with AMap integration and core UI modules.
Integrate高德 SDK with simulator-safe build flags, add map views for operating area and punch points, refactor main tabs and key feature screens to UIKit with Diffable lists, and document Swift concurrency defaults in AGENTS.md.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-26 15:16:12 +08:00

161 lines
5.7 KiB
Swift

//
// 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)
/// UI
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) }
}
/// UI UI
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)
}
/// render
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()
/// UI
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()
}
}
/// Header UI
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
}
/// tableCount
override func tableRowCount() -> Int {
viewModel.displayRows.count
}
/// Cell
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)"
)
}
/// Content
override func reloadContent() async {
await viewModel.reload(
inviteAPI: services.inviteAPI,
walletAPI: services.walletAPI,
refresh: true
)
updateSummary()
}
/// Summary
private func updateSummary() {
summaryLabel.text = "累计奖励 \(viewModel.totalRewardText) · 可提现 \(viewModel.withdrawableText)"
navigationItem.rightBarButtonItem?.title = viewModel.tab == .invite ? "奖励明细" : "邀请用户"
}
/// toggleTab
@objc private func toggleTab() {
Task {
let next: InviteRecordTab = viewModel.tab == .invite ? .reward : .invite
await viewModel.selectTab(next, inviteAPI: services.inviteAPI, walletAPI: services.walletAPI)
}
}
}