// // 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 } }