Add scenic selection and cooperation order flows aligned with Android.
Upgrade scenic picker with search, location sorting, permission apply/status, and blocking home dialog; add cooperation order module and order source picker improvements with unit tests. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
208
suixinkan/UI/Home/PermissionApplyViewController.swift
Normal file
208
suixinkan/UI/Home/PermissionApplyViewController.swift
Normal file
@ -0,0 +1,208 @@
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user