Advance UIKit rewrite with AMap integration and core UI modules.
Integrate高德 SDK with simulator-safe build flags, add map views for operating area and punch points, refactor main tabs and key feature screens to UIKit with Diffable lists, and document Swift concurrency defaults in AGENTS.md. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -14,6 +14,7 @@ struct ScenicAreaNode: Decodable, Equatable, Identifiable {
|
||||
let name: String
|
||||
let children: [ScenicAreaNode]
|
||||
|
||||
/// 枚举,定义相关常量或状态。
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case code
|
||||
@ -44,6 +45,7 @@ struct ScenicAreaNode: Decodable, Equatable, Identifiable {
|
||||
struct ScenicApplicationPendingsResponse: Decodable, Equatable {
|
||||
let items: [ScenicApplicationPendingResponse]
|
||||
|
||||
/// 枚举,定义相关常量或状态。
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case items
|
||||
case list
|
||||
@ -83,6 +85,7 @@ struct ScenicApplicationPendingResponse: Decodable, Equatable, Identifiable {
|
||||
let auditNote: String?
|
||||
let createdAt: String
|
||||
|
||||
/// 枚举,定义相关常量或状态。
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case code
|
||||
@ -171,6 +174,7 @@ struct ScenicApplicationSubmitRequest: Encodable, Equatable {
|
||||
let remark: String
|
||||
let scenicId: Int
|
||||
|
||||
/// 枚举,定义相关常量或状态。
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case scenicName = "scenic_name"
|
||||
case scenicImages = "scenic_images"
|
||||
@ -196,6 +200,7 @@ struct RoleApplyPendingResponse: Decodable, Equatable, Hashable, Identifiable {
|
||||
let auditedAt: String?
|
||||
let auditNote: String?
|
||||
|
||||
/// 枚举,定义相关常量或状态。
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case code
|
||||
@ -259,6 +264,7 @@ struct RoleApplyScenicItem: Decodable, Equatable, Hashable, Identifiable {
|
||||
let id: Int
|
||||
let name: String
|
||||
|
||||
/// 枚举,定义相关常量或状态。
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case name
|
||||
@ -283,6 +289,7 @@ struct RoleApplySubmitRequest: Encodable, Equatable {
|
||||
let scenicId: [Int]
|
||||
let roleId: Int
|
||||
|
||||
/// 枚举,定义相关常量或状态。
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case scenicId = "scenic_id"
|
||||
case roleId = "role_id"
|
||||
@ -295,6 +302,7 @@ struct ScenicApplicationUploadPlaceholder: Encodable, Equatable {
|
||||
let fileType: String
|
||||
let fileSize: Int64
|
||||
|
||||
/// 枚举,定义相关常量或状态。
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case fileName = "file_name"
|
||||
case fileType = "file_type"
|
||||
|
||||
@ -5,20 +5,44 @@
|
||||
// Created by Codex on 2026/6/26.
|
||||
//
|
||||
|
||||
import CoreLocation
|
||||
import UIKit
|
||||
|
||||
/// 景区选择页。
|
||||
final class ScenicSelectionViewController: ModuleTableViewController {
|
||||
private let viewModel = ScenicSelectionViewModel()
|
||||
private let searchBar = UISearchBar()
|
||||
private let locationProvider = ScenicSelectionLocationProvider()
|
||||
|
||||
/// 视图加载完成后的 UI 初始化与数据绑定。
|
||||
override func viewDidLoad() {
|
||||
title = "选择景区"
|
||||
navigationItem.rightBarButtonItem = UIBarButtonItem(
|
||||
title: "定位",
|
||||
style: .plain,
|
||||
target: self,
|
||||
action: #selector(requestLocation)
|
||||
)
|
||||
super.viewDidLoad()
|
||||
setupSearchBar()
|
||||
wireViewModel(viewModel) { }
|
||||
}
|
||||
|
||||
@objc private func requestLocation() {
|
||||
viewModel.currentLocationText = "定位中..."
|
||||
locationProvider.onLocation = { [weak self] location in
|
||||
self?.viewModel.applyCurrentLocation(
|
||||
latitude: location.coordinate.latitude,
|
||||
longitude: location.coordinate.longitude
|
||||
)
|
||||
}
|
||||
locationProvider.onFailure = { [weak self] message in
|
||||
self?.services.toastCenter.show(message)
|
||||
}
|
||||
locationProvider.request()
|
||||
}
|
||||
|
||||
/// 初始化SearchBar相关 UI 或状态。
|
||||
private func setupSearchBar() {
|
||||
searchBar.placeholder = "搜索景区"
|
||||
searchBar.delegate = self
|
||||
@ -26,15 +50,18 @@ final class ScenicSelectionViewController: ModuleTableViewController {
|
||||
tableView.tableHeaderView = searchBar
|
||||
}
|
||||
|
||||
/// table行Count相关逻辑。
|
||||
override func tableRowCount() -> Int {
|
||||
viewModel.filteredItems.count
|
||||
}
|
||||
|
||||
/// 配置Cell展示内容。
|
||||
override func configureCell(_ cell: TitleSubtitleTableViewCell, at indexPath: IndexPath) {
|
||||
let item = viewModel.filteredItems[indexPath.row]
|
||||
cell.configure(title: item.name, subtitle: item.address, detail: item.distanceText)
|
||||
}
|
||||
|
||||
/// didSelectTableRow 回调处理。
|
||||
override func didSelectTableRow(at indexPath: IndexPath) {
|
||||
let item = viewModel.filteredItems[indexPath.row]
|
||||
viewModel.select(
|
||||
@ -46,6 +73,7 @@ final class ScenicSelectionViewController: ModuleTableViewController {
|
||||
navigationController?.popViewController(animated: true)
|
||||
}
|
||||
|
||||
/// 刷新Content。
|
||||
override func reloadContent() async {
|
||||
viewModel.reload(from: services.accountContext)
|
||||
}
|
||||
@ -54,6 +82,7 @@ final class ScenicSelectionViewController: ModuleTableViewController {
|
||||
extension ScenicSelectionViewModel: ViewModelBindable {}
|
||||
|
||||
extension ScenicSelectionViewController: UISearchBarDelegate {
|
||||
/// searchBar相关逻辑。
|
||||
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
|
||||
viewModel.searchQuery = searchText
|
||||
}
|
||||
@ -63,6 +92,7 @@ extension ScenicSelectionViewController: UISearchBarDelegate {
|
||||
final class PermissionApplyViewController: ModuleTableViewController {
|
||||
private let viewModel = PermissionApplyViewModel()
|
||||
|
||||
/// 视图加载完成后的 UI 初始化与数据绑定。
|
||||
override func viewDidLoad() {
|
||||
title = "权限申请"
|
||||
navigationItem.rightBarButtonItem = UIBarButtonItem(
|
||||
@ -75,17 +105,21 @@ final class PermissionApplyViewController: ModuleTableViewController {
|
||||
wireViewModel(viewModel) { }
|
||||
}
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int { 2 }
|
||||
/// 返回列表 section 数量。
|
||||
override func numberOfTableSections() -> Int { 2 }
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
/// 返回指定 section 行数。
|
||||
override func tableRowCount(in section: Int) -> Int {
|
||||
section == 0 ? viewModel.roleOptions.count : viewModel.scenicOptions.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
/// section 标题。
|
||||
override func tableSectionTitle(for section: Int) -> String? {
|
||||
section == 0 ? "选择角色" : "选择景区"
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
/// 提供自定义 Cell。
|
||||
override func tableCell(for indexPath: IndexPath, row: ModuleTableRow, in tableView: UITableView) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(
|
||||
withIdentifier: TitleSubtitleTableViewCell.reuseIdentifier,
|
||||
for: indexPath
|
||||
@ -102,8 +136,8 @@ final class PermissionApplyViewController: ModuleTableViewController {
|
||||
return cell
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
/// 处理行选中。
|
||||
override func didSelectTableRow(at indexPath: IndexPath) {
|
||||
if indexPath.section == 0 {
|
||||
viewModel.selectRole(id: viewModel.roleOptions[indexPath.row].id)
|
||||
Task { await viewModel.loadScenicListIfNeeded(api: services.scenicPermissionAPI, force: true) }
|
||||
@ -112,6 +146,7 @@ final class PermissionApplyViewController: ModuleTableViewController {
|
||||
}
|
||||
}
|
||||
|
||||
/// 刷新Content。
|
||||
override func reloadContent() async {
|
||||
viewModel.bootstrap(rolePermissions: services.permissionContext.rolePermissions)
|
||||
if viewModel.selectedRoleId != nil {
|
||||
@ -119,6 +154,7 @@ final class PermissionApplyViewController: ModuleTableViewController {
|
||||
}
|
||||
}
|
||||
|
||||
/// 提交。
|
||||
@objc private func submit() {
|
||||
Task {
|
||||
await viewModel.submit(api: services.scenicPermissionAPI)
|
||||
@ -138,16 +174,19 @@ extension PermissionApplyViewModel: ViewModelBindable {}
|
||||
final class PermissionApplyStatusViewController: ModuleTableViewController {
|
||||
private let viewModel = PermissionApplyStatusViewModel()
|
||||
|
||||
/// 视图加载完成后的 UI 初始化与数据绑定。
|
||||
override func viewDidLoad() {
|
||||
title = "申请状态"
|
||||
super.viewDidLoad()
|
||||
viewModel.onChange = { [weak self] in self?.reloadTable() }
|
||||
}
|
||||
|
||||
/// table行Count相关逻辑。
|
||||
override func tableRowCount() -> Int {
|
||||
viewModel.pending == nil ? 0 : 4
|
||||
}
|
||||
|
||||
/// 配置Cell展示内容。
|
||||
override func configureCell(_ cell: TitleSubtitleTableViewCell, at indexPath: IndexPath) {
|
||||
guard let pending = viewModel.pending else { return }
|
||||
switch indexPath.row {
|
||||
@ -158,6 +197,7 @@ final class PermissionApplyStatusViewController: ModuleTableViewController {
|
||||
}
|
||||
}
|
||||
|
||||
/// 刷新Content。
|
||||
override func reloadContent() async {
|
||||
await viewModel.load(api: services.scenicPermissionAPI, applyCode: nil)
|
||||
}
|
||||
@ -168,6 +208,7 @@ final class ScenicApplicationViewController: ModuleTableViewController {
|
||||
private let viewModel = ScenicApplicationViewModel()
|
||||
private let nameField = UITextField()
|
||||
|
||||
/// 视图加载完成后的 UI 初始化与数据绑定。
|
||||
override func viewDidLoad() {
|
||||
title = "景区申请"
|
||||
navigationItem.rightBarButtonItem = UIBarButtonItem(
|
||||
@ -188,8 +229,10 @@ final class ScenicApplicationViewController: ModuleTableViewController {
|
||||
}
|
||||
}
|
||||
|
||||
/// table行Count相关逻辑。
|
||||
override func tableRowCount() -> Int { 3 }
|
||||
|
||||
/// 配置Cell展示内容。
|
||||
override func configureCell(_ cell: TitleSubtitleTableViewCell, at indexPath: IndexPath) {
|
||||
switch indexPath.row {
|
||||
case 0: cell.configure(title: "省份", subtitle: viewModel.selectedProvince)
|
||||
@ -198,10 +241,12 @@ final class ScenicApplicationViewController: ModuleTableViewController {
|
||||
}
|
||||
}
|
||||
|
||||
/// 刷新Content。
|
||||
override func reloadContent() async {
|
||||
await viewModel.loadInitial(api: services.scenicPermissionAPI)
|
||||
}
|
||||
|
||||
/// 提交。
|
||||
@objc private func submit() {
|
||||
viewModel.scenicName = nameField.text ?? ""
|
||||
Task {
|
||||
|
||||
Reference in New Issue
Block a user