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>
161 lines
5.7 KiB
Swift
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
|
|
}
|
|
|
|
/// table行Count相关逻辑。
|
|
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)
|
|
}
|
|
}
|
|
}
|