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:
@ -10,9 +10,13 @@ import Foundation
|
||||
/// 飞手认证服务协议,定义认证详情、短信、提交和编辑接口。
|
||||
@MainActor
|
||||
protocol PilotCertificationServing {
|
||||
/// 飞手详情相关逻辑。
|
||||
func flyerDetail() async throws -> FlyerDetailResponse
|
||||
/// 飞手Send验证码相关逻辑。
|
||||
func flyerSendCode(phone: String) async throws
|
||||
/// 飞手提交相关逻辑。
|
||||
func flyerApply(_ request: FlyerApplyRequest) async throws
|
||||
/// 飞手编辑相关逻辑。
|
||||
func flyerEdit(_ request: FlyerEditRequest) async throws
|
||||
}
|
||||
|
||||
|
||||
@ -48,6 +48,7 @@ struct FlyerDetailResponse: Decodable, Equatable {
|
||||
let realnameStatusText: String
|
||||
let certificationLogs: [FlyerCertificationLogItem]
|
||||
|
||||
/// 枚举,定义相关常量或状态。
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case name = "flyer_nickname"
|
||||
@ -72,6 +73,7 @@ struct FlyerDetailResponse: Decodable, Equatable {
|
||||
case certificationLogs = "flyers_certification_logs"
|
||||
}
|
||||
|
||||
/// 初始化实例。
|
||||
init(
|
||||
id: Int = 0,
|
||||
name: String = "",
|
||||
@ -118,6 +120,7 @@ struct FlyerDetailResponse: Decodable, Equatable {
|
||||
self.certificationLogs = certificationLogs
|
||||
}
|
||||
|
||||
/// 初始化实例。
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
id = try container.pilotDecodeLossyInt(forKey: .id) ?? 0
|
||||
@ -155,6 +158,7 @@ struct FlyerCertificationLogItem: Decodable, Identifiable, Equatable {
|
||||
let remark: String
|
||||
let createdAt: String
|
||||
|
||||
/// 枚举,定义相关常量或状态。
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case flyerId = "flyer_id"
|
||||
@ -166,6 +170,7 @@ struct FlyerCertificationLogItem: Decodable, Identifiable, Equatable {
|
||||
case createdAt = "created_at"
|
||||
}
|
||||
|
||||
/// 初始化实例。
|
||||
init(
|
||||
id: Int = 0,
|
||||
flyerId: Int = 0,
|
||||
@ -186,6 +191,7 @@ struct FlyerCertificationLogItem: Decodable, Identifiable, Equatable {
|
||||
self.createdAt = createdAt
|
||||
}
|
||||
|
||||
/// 初始化实例。
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
id = try container.pilotDecodeLossyInt(forKey: .id) ?? 0
|
||||
@ -218,6 +224,7 @@ struct FlyerApplyRequest: Encodable, Equatable {
|
||||
let contactPhone: String
|
||||
let code: String
|
||||
|
||||
/// 枚举,定义相关常量或状态。
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case name
|
||||
case realnameStatus = "realname_status"
|
||||
@ -248,6 +255,7 @@ struct FlyerEditRequest: Encodable, Equatable {
|
||||
let contactPhone: String
|
||||
let code: String
|
||||
|
||||
/// 枚举,定义相关常量或状态。
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case name
|
||||
@ -277,6 +285,7 @@ enum PilotCertificationValidationError: LocalizedError, Equatable {
|
||||
}
|
||||
|
||||
private extension KeyedDecodingContainer {
|
||||
/// pilot解码宽松字符串相关逻辑。
|
||||
func pilotDecodeLossyString(forKey key: Key) throws -> String {
|
||||
if let value = try? decodeIfPresent(String.self, forKey: key) {
|
||||
return value
|
||||
@ -293,6 +302,7 @@ private extension KeyedDecodingContainer {
|
||||
return ""
|
||||
}
|
||||
|
||||
/// pilot解码宽松整数相关逻辑。
|
||||
func pilotDecodeLossyInt(forKey key: Key) throws -> Int? {
|
||||
if let value = try? decodeIfPresent(Int.self, forKey: key) {
|
||||
return value
|
||||
|
||||
@ -13,6 +13,7 @@ final class PilotCertificationViewController: ModuleTableViewController {
|
||||
private let viewModel = PilotCertificationViewModel()
|
||||
private let statusLabel = UILabel()
|
||||
|
||||
/// 视图加载完成后的 UI 初始化与数据绑定。
|
||||
override func viewDidLoad() {
|
||||
title = "飞手认证"
|
||||
navigationItem.rightBarButtonItem = UIBarButtonItem(
|
||||
@ -26,6 +27,7 @@ final class PilotCertificationViewController: ModuleTableViewController {
|
||||
wireViewModel(viewModel) { [weak self] in self?.updateHeader() }
|
||||
}
|
||||
|
||||
/// 初始化Header相关 UI 或状态。
|
||||
private func setupHeader() {
|
||||
statusLabel.numberOfLines = 0
|
||||
statusLabel.font = .systemFont(ofSize: 14)
|
||||
@ -34,17 +36,21 @@ final class PilotCertificationViewController: ModuleTableViewController {
|
||||
tableView.tableHeaderView = statusLabel
|
||||
}
|
||||
|
||||
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 ? formRows.count : 1
|
||||
}
|
||||
|
||||
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
|
||||
@ -58,8 +64,8 @@ final class PilotCertificationViewController: 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 == 1 {
|
||||
Task {
|
||||
do {
|
||||
@ -72,11 +78,13 @@ final class PilotCertificationViewController: ModuleTableViewController {
|
||||
}
|
||||
}
|
||||
|
||||
/// 刷新Content。
|
||||
override func reloadContent() async {
|
||||
await viewModel.load(api: services.pilotCertificationAPI, realNameAPI: services.profileAPI)
|
||||
updateHeader()
|
||||
}
|
||||
|
||||
/// 提交。
|
||||
@objc private func submit() {
|
||||
Task {
|
||||
do {
|
||||
@ -102,6 +110,7 @@ final class PilotCertificationViewController: ModuleTableViewController {
|
||||
]
|
||||
}
|
||||
|
||||
/// 更新Header状态。
|
||||
private func updateHeader() {
|
||||
let status = viewModel.auditStatusText
|
||||
statusLabel.text = "状态:\(status)\n\(viewModel.statusMessage ?? "")"
|
||||
|
||||
@ -10,6 +10,7 @@ import Foundation
|
||||
@MainActor
|
||||
/// 飞手认证实名认证读取协议,便于 ViewModel 单测替换。
|
||||
protocol PilotRealNameServing {
|
||||
/// realNameInfo相关逻辑。
|
||||
func realNameInfo() async throws -> RealNameInfoResponse
|
||||
}
|
||||
|
||||
@ -167,6 +168,7 @@ final class PilotCertificationViewModel {
|
||||
flyer?.certificationLogs.last { $0.action == 2 || $0.action == 3 }
|
||||
}
|
||||
|
||||
/// apply 业务逻辑。
|
||||
private func apply(_ flyer: FlyerDetailResponse) {
|
||||
self.flyer = flyer
|
||||
name = flyer.name
|
||||
@ -183,6 +185,7 @@ final class PilotCertificationViewModel {
|
||||
uploadProgress = nil
|
||||
}
|
||||
|
||||
/// upload待处理Certificate图片相关逻辑。
|
||||
private func uploadPendingCertificateImage(uploader: any OSSUploadServing, scenicId: Int?) async throws {
|
||||
guard let data = pendingCertificateImageData else { return }
|
||||
guard let scenicId, scenicId > 0 else {
|
||||
@ -203,6 +206,7 @@ final class PilotCertificationViewModel {
|
||||
pendingCertificateFileName = nil
|
||||
}
|
||||
|
||||
/// 创建ApplyRequest实例。
|
||||
private func makeApplyRequest() -> FlyerApplyRequest {
|
||||
FlyerApplyRequest(
|
||||
name: name.trimmedForPilot,
|
||||
@ -219,6 +223,7 @@ final class PilotCertificationViewModel {
|
||||
)
|
||||
}
|
||||
|
||||
/// 创建EditRequest实例。
|
||||
private func makeEditRequest() -> FlyerEditRequest {
|
||||
let apply = makeApplyRequest()
|
||||
return FlyerEditRequest(
|
||||
@ -237,6 +242,7 @@ final class PilotCertificationViewModel {
|
||||
)
|
||||
}
|
||||
|
||||
/// statusName相关逻辑。
|
||||
private static func statusName(for status: Int) -> String {
|
||||
switch status {
|
||||
case 1: "审核中"
|
||||
@ -254,6 +260,7 @@ final class PilotCertificationViewModel {
|
||||
return formatter
|
||||
}()
|
||||
|
||||
/// date 业务逻辑。
|
||||
private static func date(from value: String) -> Date? {
|
||||
let text = String(value.prefix(10))
|
||||
guard !text.isEmpty else { return nil }
|
||||
@ -261,9 +268,11 @@ final class PilotCertificationViewModel {
|
||||
}
|
||||
}
|
||||
|
||||
/// PilotEmptyRealName服务协议,定义模块对外能力。
|
||||
private struct PilotEmptyRealNameServing: PilotRealNameServing {
|
||||
let info: RealNameInfo?
|
||||
|
||||
/// realNameInfo相关逻辑。
|
||||
func realNameInfo() async throws -> RealNameInfoResponse {
|
||||
RealNameInfoResponse(realNameInfo: info)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user