完善实名认证、钱包与打卡点模块 UI 与业务逻辑。
对齐 Android 实名认证流程与审核页、钱包提现/积分兑换界面,优化打卡点列表与详情交互,并补充相关资源、主题 token 与单元测试。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -390,6 +390,13 @@ struct PunchPointUploadDialogState: Sendable, Equatable {
|
||||
let progress: Int
|
||||
}
|
||||
|
||||
/// 打卡点状态标签的文案与配色。
|
||||
struct PunchPointStatusPresentation: Sendable, Equatable {
|
||||
let title: String
|
||||
let backgroundColor: UInt
|
||||
let textColor: UInt
|
||||
}
|
||||
|
||||
/// 打卡点 UI 文案与格式化工具。
|
||||
enum PunchPointDisplayFormatter {
|
||||
/// 手机号脱敏。
|
||||
@ -401,32 +408,40 @@ enum PunchPointDisplayFormatter {
|
||||
return "\(prefix)****\(suffix)"
|
||||
}
|
||||
|
||||
/// 审核/运营标签背景色。
|
||||
static func statusColors(_ status: Int) -> (background: UInt, text: UInt) {
|
||||
/// 运营状态标签,严格对应 Android `OperatingTag`。
|
||||
static func operatingStatus(_ status: Int) -> PunchPointStatusPresentation {
|
||||
switch status {
|
||||
case 1:
|
||||
return (0xF0FDF4, 0x09BE4F)
|
||||
return PunchPointStatusPresentation(title: "营运中", backgroundColor: 0xF0FDF4, textColor: 0x22C55E)
|
||||
case 2:
|
||||
return (0xFFF0E2, 0xFF7B00)
|
||||
case 3:
|
||||
return (0xEFF6FF, 0x0073FF)
|
||||
case 4:
|
||||
return (0xFFE7E7, 0xEF4444)
|
||||
return PunchPointStatusPresentation(title: "已暂停", backgroundColor: 0xF4F4F4, textColor: 0x4B5563)
|
||||
default:
|
||||
return (0xF4F4F4, 0x666666)
|
||||
return PunchPointStatusPresentation(title: "未上线", backgroundColor: 0xF4F4F4, textColor: 0x4B5563)
|
||||
}
|
||||
}
|
||||
|
||||
/// 审核状态标签,严格对应 Android `StatusLabelChip`。
|
||||
static func auditStatus(_ status: Int) -> PunchPointStatusPresentation {
|
||||
switch status {
|
||||
case 1, 2:
|
||||
return PunchPointStatusPresentation(title: "已通过", backgroundColor: 0xF0FDF4, textColor: 0x22C55E)
|
||||
case 4:
|
||||
return PunchPointStatusPresentation(title: "已驳回", backgroundColor: 0xFFE7E7, textColor: 0xEF4444)
|
||||
default:
|
||||
return PunchPointStatusPresentation(title: "待审核", backgroundColor: 0xFFF0E2, textColor: 0xFF7B00)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension KeyedDecodingContainer {
|
||||
func decodeFlexibleInt64(forKey key: Key) throws -> Int64? {
|
||||
if let value = try decodeIfPresent(Int64.self, forKey: key) {
|
||||
if let value = try? decodeIfPresent(Int64.self, forKey: key) {
|
||||
return value
|
||||
}
|
||||
if let value = try decodeIfPresent(Int.self, forKey: key) {
|
||||
if let value = try? decodeIfPresent(Int.self, forKey: key) {
|
||||
return Int64(value)
|
||||
}
|
||||
if let value = try decodeIfPresent(String.self, forKey: key) {
|
||||
if let value = try? decodeIfPresent(String.self, forKey: key) {
|
||||
return Int64(value)
|
||||
}
|
||||
return nil
|
||||
|
||||
@ -29,11 +29,13 @@ final class PunchPointListViewModel {
|
||||
var onStateChange: (() -> Void)?
|
||||
var onShowMessage: ((String) -> Void)?
|
||||
var onShowQRCode: ((String) -> Void)?
|
||||
var onBeginDeleteAnimation: ((Int64) -> Void)?
|
||||
|
||||
private var currentPage = 1
|
||||
private var totalCount = 0
|
||||
private let pageSize = 10
|
||||
private let currentScenicIdProvider: () -> Int
|
||||
private var pendingDeleteIDs = Set<Int64>()
|
||||
|
||||
/// 初始化打卡点列表 ViewModel。
|
||||
init(currentScenicIdProvider: @escaping () -> Int = { AppStore.shared.session.currentScenicId }) {
|
||||
@ -81,12 +83,11 @@ final class PunchPointListViewModel {
|
||||
|
||||
/// 删除打卡点。
|
||||
func delete(item: PunchPointItem, api: any PunchPointAPIProtocol) async {
|
||||
guard !pendingDeleteIDs.contains(item.id) else { return }
|
||||
do {
|
||||
try await api.delete(id: item.id)
|
||||
items.removeAll { $0.id == item.id }
|
||||
totalCount = max(0, totalCount - 1)
|
||||
canLoadMore = items.count < totalCount
|
||||
notifyStateChange()
|
||||
pendingDeleteIDs.insert(item.id)
|
||||
onBeginDeleteAnimation?(item.id)
|
||||
} catch is CancellationError {
|
||||
return
|
||||
} catch {
|
||||
@ -94,6 +95,15 @@ final class PunchPointListViewModel {
|
||||
}
|
||||
}
|
||||
|
||||
/// 删除动画完成后,从列表数据中正式移除条目。
|
||||
func completeDeleteAnimation(id: Int64) {
|
||||
guard pendingDeleteIDs.remove(id) != nil else { return }
|
||||
items.removeAll { $0.id == id }
|
||||
totalCount = max(0, totalCount - 1)
|
||||
canLoadMore = items.count < totalCount
|
||||
notifyStateChange()
|
||||
}
|
||||
|
||||
private func load(reset: Bool, showLoading: Bool, api: any PunchPointAPIProtocol) async {
|
||||
if reset {
|
||||
currentPage = 1
|
||||
|
||||
Reference in New Issue
Block a user