Files
suixinkan_uikit/suixinkan/UI/Home/ScenicSelectionViewController.swift
汉秋 6336feff27 完善景区排队设置页与全局按钮配置迁移。
重构排队设置与变更日志交互,补充 Overlay 与资源,并将多页面主操作按钮统一到 UIButton Configuration,同步更新相关单元测试。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-14 16:21:53 +08:00

160 lines
5.7 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// ScenicSelectionViewController.swift
// suixinkan
//
import SnapKit
import UIKit
/// Android `ScenicSelectionScreen`
final class ScenicSelectionViewController: BaseViewController, UITableViewDataSource, UITableViewDelegate {
var onSelectionComplete: (() -> Void)?
private let viewModel: ScenicSelectionViewModel
private let homeAPI: HomeAPI
private let bannerView = ScenicPermissionBannerView()
private let searchBar = ScenicSelectionSearchBar()
private let locationBar = ScenicCurrentLocationBar()
private let tableView = UITableView(frame: .zero, style: .plain)
private let emptyLabel = UILabel()
init(viewModel: ScenicSelectionViewModel = ScenicSelectionViewModel(), homeAPI: HomeAPI? = nil) {
self.viewModel = viewModel
self.homeAPI = homeAPI ?? NetworkServices.shared.homeAPI
super.init(nibName: nil, bundle: nil)
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func setupNavigationBar() {
title = "景区选择"
navigationController?.setNavigationBarHidden(false, animated: false)
}
override func setupUI() {
view.backgroundColor = .white
tableView.backgroundColor = .clear
tableView.separatorStyle = .none
tableView.dataSource = self
tableView.delegate = self
tableView.register(ScenicSpotCell.self, forCellReuseIdentifier: ScenicSpotCell.reuseIdentifier)
tableView.keyboardDismissMode = .onDrag
emptyLabel.text = "暂无景区"
emptyLabel.font = .systemFont(ofSize: 14)
emptyLabel.textColor = AppColor.textTertiary
emptyLabel.textAlignment = .center
emptyLabel.isHidden = true
let contentStack = UIStackView(arrangedSubviews: [searchBar, locationBar])
contentStack.axis = .vertical
contentStack.spacing = 16
contentStack.isLayoutMarginsRelativeArrangement = true
contentStack.layoutMargins = UIEdgeInsets(top: 0, left: 16, bottom: 16, right: 16)
let headerStack = UIStackView(arrangedSubviews: [bannerView, contentStack])
headerStack.axis = .vertical
headerStack.spacing = 16
headerStack.setContentHuggingPriority(.required, for: .vertical)
headerStack.setContentCompressionResistancePriority(.required, for: .vertical)
view.addSubview(headerStack)
view.addSubview(tableView)
view.addSubview(emptyLabel)
headerStack.snp.makeConstraints { make in
make.top.equalTo(view.safeAreaLayoutGuide)
make.leading.trailing.equalToSuperview()
}
tableView.snp.makeConstraints { make in
make.top.equalTo(headerStack.snp.bottom)
make.leading.trailing.bottom.equalToSuperview()
}
emptyLabel.snp.makeConstraints { make in
make.center.equalTo(tableView)
}
}
override func bindActions() {
viewModel.onStateChange = { [weak self] in
Task { @MainActor in self?.applyViewModel() }
}
viewModel.onShowMessage = { [weak self] message in
Task { @MainActor in self?.showToast(message) }
}
viewModel.onSelectionComplete = { [weak self] in
Task { @MainActor in
self?.onSelectionComplete?()
self?.navigationController?.popViewController(animated: true)
}
}
viewModel.onNavigateApply = { [weak self] in
Task { @MainActor in self?.pushPermissionApply() }
}
viewModel.onNavigateApplyStatus = { [weak self] applyCode in
Task { @MainActor in self?.pushPermissionApplyStatus(applyCode: applyCode) }
}
bannerView.onApplyNow = { [weak self] in
guard let self else { return }
Task { await self.viewModel.onApplyNow(api: self.homeAPI) }
}
searchBar.onSearchTextChange = { [weak self] query in
self?.viewModel.onSearchQueryChange(query)
}
locationBar.onRelocate = { [weak self] in
guard let self else { return }
Task { await self.viewModel.startLocation() }
}
viewModel.loadFromLocal()
Task { await viewModel.startLocation() }
}
private func applyViewModel() {
locationBar.apply(locationText: viewModel.currentLocationText, isLocating: viewModel.isLocating)
emptyLabel.isHidden = !viewModel.spots.isEmpty
tableView.reloadData()
if viewModel.isLoading {
showLoading()
} else {
hideLoading()
}
}
private func pushPermissionApply(pendingData: RoleApplyPendingResponse? = nil) {
let controller = PermissionApplyViewController(pendingData: pendingData)
navigationController?.pushViewController(controller, animated: true)
}
private func pushPermissionApplyStatus(applyCode: String) {
let controller = PermissionApplyStatusViewController(applyCode: applyCode)
navigationController?.pushViewController(controller, animated: true)
}
// MARK: - UITableView
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
viewModel.spots.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(
withIdentifier: ScenicSpotCell.reuseIdentifier,
for: indexPath
) as! ScenicSpotCell
let spot = viewModel.spots[indexPath.row]
cell.apply(spot)
cell.onTap = { [weak self] in
self?.viewModel.selectSpot(spot)
}
return cell
}
}