添加景区选择与合作订单流程并对齐 Android

This commit is contained in:
2026-07-07 10:48:43 +08:00
parent ef1d3b4af5
commit c4057537d2
50 changed files with 5603 additions and 191 deletions

View File

@ -6,16 +6,23 @@
import SnapKit
import UIKit
/// 使 role-permission
final class ScenicSelectionViewController: BaseViewController {
/// Android `ScenicSelectionScreen`
final class ScenicSelectionViewController: BaseViewController, UITableViewDataSource, UITableViewDelegate {
var onSelected: (() -> Void)?
var onSelectionComplete: (() -> Void)?
private let scenicList: [ScenicInfo]
private let tableView = UITableView(frame: .zero, style: .insetGrouped)
private let viewModel: ScenicSelectionViewModel
private let homeAPI: HomeAPI
init(scenicList: [ScenicInfo]) {
self.scenicList = scenicList
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 = NetworkServices.shared.homeAPI) {
self.viewModel = viewModel
self.homeAPI = homeAPI
super.init(nibName: nil, bundle: nil)
}
@ -25,61 +32,122 @@ final class ScenicSelectionViewController: BaseViewController {
}
override func setupNavigationBar() {
title = "选择景区"
navigationItem.leftBarButtonItem = UIBarButtonItem(
barButtonSystemItem: .close,
target: self,
action: #selector(closeTapped)
)
title = "景区选择"
navigationController?.setNavigationBarHidden(false, animated: false)
}
override func setupUI() {
view.backgroundColor = UIColor(hex: 0xF7FAFF)
view.backgroundColor = .white
tableView.backgroundColor = .clear
tableView.separatorStyle = .none
tableView.dataSource = self
tableView.delegate = self
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
view.addSubview(tableView)
}
tableView.register(ScenicSpotCell.self, forCellReuseIdentifier: ScenicSpotCell.reuseIdentifier)
tableView.keyboardDismissMode = .onDrag
override func setupConstraints() {
emptyLabel.text = "暂无景区"
emptyLabel.font = .systemFont(ofSize: 14)
emptyLabel.textColor = AppColor.textTertiary
emptyLabel.textAlignment = .center
emptyLabel.isHidden = true
let headerStack = UIStackView(arrangedSubviews: [bannerView, searchBar, locationBar])
headerStack.axis = .vertical
headerStack.spacing = 16
headerStack.isLayoutMarginsRelativeArrangement = true
headerStack.layoutMargins = UIEdgeInsets(top: 0, left: 16, bottom: 16, right: 16)
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.edges.equalTo(view.safeAreaLayoutGuide)
make.top.equalTo(headerStack.snp.bottom)
make.leading.trailing.bottom.equalToSuperview()
}
emptyLabel.snp.makeConstraints { make in
make.center.equalTo(tableView)
}
}
@objc private func closeTapped() {
dismiss(animated: true)
}
}
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)
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
extension ScenicSelectionViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
scenicList.count
viewModel.spots.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
var config = cell.defaultContentConfiguration()
config.text = scenicList[indexPath.row].name
cell.contentConfiguration = config
cell.accessoryType = scenicList[indexPath.row].id == AppStore.shared.currentScenicId ? .checkmark : .none
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
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let scenic = scenicList[indexPath.row]
AppStore.shared.currentScenicId = scenic.id
AppStore.shared.currentScenicName = scenic.name
NotificationCenter.default.post(
name: NotificationName.scenicDidChange,
object: nil,
userInfo: [
NotificationUserInfoKey.scenicId: scenic.id,
NotificationUserInfoKey.scenicName: scenic.name,
]
)
onSelected?()
dismiss(animated: true)
}
}