// // PermissionApplyViewController.swift // suixinkan // import SnapKit import UIKit /// 权限申请页,对齐 Android `PermissionApplyScreen`。 final class PermissionApplyViewController: BaseViewController { private let viewModel: PermissionApplyViewModel private let homeAPI: HomeAPI private let pendingData: RoleApplyPendingResponse? private let scrollView = UIScrollView() private let contentStack = UIStackView() private let bannerView = PermissionApplyBannerView() private let roleSelector = PermissionFormSelectorRow(title: "角色信息", subtitle: "单选 · 请选择您需要申请权限的角色", placeholder: "请选择角色") private let existingScenicTags = PermissionSelectedTagsView() private let scenicSelector = PermissionFormSelectorRow(title: "新增景区", subtitle: "多选 · 请选择需要新增权限的景区", placeholder: "请选择景区") private let selectedScenicTags = PermissionSelectedTagsView() private let submitButton = PermissionSubmitButton() init( pendingData: RoleApplyPendingResponse? = nil, viewModel: PermissionApplyViewModel = PermissionApplyViewModel(), homeAPI: HomeAPI = NetworkServices.shared.homeAPI ) { self.pendingData = pendingData self.viewModel = viewModel self.homeAPI = homeAPI super.init(nibName: nil, bundle: nil) } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func setupNavigationBar() { title = "申请权限" } override func setupUI() { contentStack.axis = .vertical contentStack.spacing = 16 let card = UIView() card.backgroundColor = .white card.layer.cornerRadius = 8 let cardStack = UIStackView(arrangedSubviews: [roleSelector, existingScenicTags, scenicSelector, selectedScenicTags]) cardStack.axis = .vertical cardStack.spacing = 20 card.addSubview(cardStack) cardStack.snp.makeConstraints { make in make.edges.equalToSuperview().inset(16) } contentStack.addArrangedSubview(bannerView) contentStack.addArrangedSubview(card) scrollView.addSubview(contentStack) view.addSubview(scrollView) view.addSubview(submitButton) contentStack.snp.makeConstraints { make in make.edges.equalToSuperview().inset(UIEdgeInsets(top: 0, left: 16, bottom: 16, right: 16)) make.width.equalTo(scrollView).offset(-32) } scrollView.snp.makeConstraints { make in make.top.leading.trailing.equalTo(view.safeAreaLayoutGuide) make.bottom.equalTo(submitButton.snp.top).offset(-12) } submitButton.snp.makeConstraints { make in make.leading.trailing.equalToSuperview().inset(16) make.bottom.equalTo(view.safeAreaLayoutGuide).offset(-12) } } 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.onSubmitSuccess = { [weak self] applyCode in Task { @MainActor in self?.handleSubmitSuccess(applyCode: applyCode) } } bannerView.onApplyNewScenic = { [weak self] in self?.showToast("功能开发中") } roleSelector.onTap = { [weak self] in self?.presentRolePicker() } scenicSelector.onTap = { [weak self] in guard let self else { return } self.viewModel.showScenicPickerSheet() if self.viewModel.availableScenics.isEmpty { Task { await self.viewModel.loadAvailableScenics(api: self.homeAPI) } } self.presentScenicPickerIfNeeded() } submitButton.addTarget(self, action: #selector(submitTapped), for: .touchUpInside) if let pendingData { viewModel.initWithPendingData(pendingData) Task { await viewModel.loadAvailableScenics(api: homeAPI) } } applyViewModel() } private func applyViewModel() { if viewModel.selectedRoleName.isEmpty { roleSelector.setValue("请选择角色", isPlaceholder: true) existingScenicTags.isHidden = true } else { roleSelector.setValue(viewModel.selectedRoleName, isPlaceholder: false) if let roleId = viewModel.selectedRoleId { let names = viewModel.existingScenics(for: roleId).map(\.name) existingScenicTags.apply(title: "已开通景区", names: names, onRemove: nil) } } if viewModel.selectedScenicNames.isEmpty { scenicSelector.setValue("请选择景区", isPlaceholder: true) selectedScenicTags.isHidden = true } else { scenicSelector.setValue("已选 \(viewModel.selectedScenicNames.count) 个景区", isPlaceholder: false) selectedScenicTags.apply( title: "待申请景区", names: viewModel.selectedScenicNames ) { [weak self] index in guard let self else { return } let id = self.viewModel.selectedScenicIds[index] let name = self.viewModel.selectedScenicNames[index] self.viewModel.removeSelectedScenic(id: id, name: name) } } submitButton.setSubmitting(viewModel.isSubmitting) presentScenicPickerIfNeeded() presentRolePickerIfNeeded() } private func presentRolePickerIfNeeded() { guard viewModel.showRolePicker else { return } viewModel.hideRolePickerSheet() presentRolePicker() } private func presentRolePicker() { let alert = UIAlertController(title: "选择角色", message: nil, preferredStyle: .actionSheet) viewModel.availableRoles.forEach { role in alert.addAction(UIAlertAction(title: role.name, style: .default) { [weak self] _ in self?.viewModel.selectRole(role) Task { await self?.viewModel.loadAvailableScenics(api: self?.homeAPI) } }) } alert.addAction(UIAlertAction(title: "取消", style: .cancel)) present(alert, animated: true) } private func presentScenicPickerIfNeeded() { guard viewModel.showScenicPicker else { return } viewModel.hideScenicPickerSheet() presentScenicPicker() } private func presentScenicPicker() { guard viewModel.selectedRoleId != nil else { showToast("请先选择角色") return } if viewModel.availableScenics.isEmpty { Task { await viewModel.loadAvailableScenics(api: homeAPI) await MainActor.run { self.presentScenicPicker() } } return } let alert = UIAlertController(title: "选择景区", message: nil, preferredStyle: .actionSheet) viewModel.availableScenics.filter { !$0.isDisabled }.forEach { scenic in let prefix = scenic.isSelected ? "✓ " : "" alert.addAction(UIAlertAction(title: "\(prefix)\(scenic.name)", style: .default) { [weak self] _ in self?.viewModel.toggleScenic(scenic) }) } alert.addAction(UIAlertAction(title: "完成", style: .cancel)) present(alert, animated: true) } @objc private func submitTapped() { Task { await viewModel.submit(api: homeAPI) } } private func handleSubmitSuccess(applyCode: String) { guard !applyCode.isEmpty else { navigationController?.popViewController(animated: true) return } var controllers = navigationController?.viewControllers ?? [] controllers.removeAll { $0 === self } let statusVC = PermissionApplyStatusViewController(applyCode: applyCode) controllers.append(statusVC) navigationController?.setViewControllers(controllers, animated: true) } }