224 lines
8.3 KiB
Swift
224 lines
8.3 KiB
Swift
//
|
||
// 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: "角色信息",
|
||
tag: "单选",
|
||
description: "请选择您需要申请权限的角色",
|
||
placeholder: "请选择角色"
|
||
)
|
||
private let newRoleTags = PermissionSelectedTagsView()
|
||
private let scenicSelector = PermissionFormSelectorRow(
|
||
title: "选择景区",
|
||
tag: "可多选",
|
||
description: "请选择您需要申请权限的景区",
|
||
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, newRoleTags, 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?.navigationController?.pushViewController(ScenicApplicationViewController(), animated: true)
|
||
}
|
||
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)
|
||
newRoleTags.isHidden = true
|
||
} else {
|
||
roleSelector.setValue(viewModel.selectedRoleName, isPlaceholder: false)
|
||
newRoleTags.apply(title: "新增角色", names: [viewModel.selectedRoleName]) { [weak self] _ in
|
||
self?.viewModel.clearRoleSelection()
|
||
}
|
||
}
|
||
|
||
if viewModel.selectedScenicNames.isEmpty {
|
||
scenicSelector.setValue("请选择景区", isPlaceholder: true)
|
||
selectedScenicTags.isHidden = true
|
||
} else {
|
||
let displayText: String
|
||
if viewModel.selectedScenicNames.count == 1 {
|
||
displayText = viewModel.selectedScenicNames[0]
|
||
} else {
|
||
displayText = "已选中\(viewModel.selectedScenicNames.count)个景区"
|
||
}
|
||
scenicSelector.setValue(displayText, 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)
|
||
}
|
||
}
|