Initial commit: suixinkan_ios UIKit rewrite project.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -0,0 +1,60 @@
|
||||
//
|
||||
// WithdrawalAuditViewController.swift
|
||||
// suixinkan
|
||||
//
|
||||
// Created by Codex on 2026/6/26.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
/// 提现审核列表页。
|
||||
final class WithdrawalAuditViewController: ModuleTableViewController {
|
||||
private let viewModel = WithdrawalAuditViewModel()
|
||||
|
||||
override func viewDidLoad() {
|
||||
title = "提现审核"
|
||||
navigationItem.rightBarButtonItem = UIBarButtonItem(
|
||||
title: "全部",
|
||||
style: .plain,
|
||||
target: self,
|
||||
action: #selector(cycleFilter)
|
||||
)
|
||||
super.viewDidLoad()
|
||||
wireViewModel(viewModel) { [weak self] in self?.updateFilterTitle() }
|
||||
}
|
||||
|
||||
override func tableRowCount() -> Int {
|
||||
viewModel.filteredRecords.count
|
||||
}
|
||||
|
||||
override func configureCell(_ cell: TitleSubtitleTableViewCell, at indexPath: IndexPath) {
|
||||
let record = viewModel.filteredRecords[indexPath.row]
|
||||
cell.configure(title: "¥\(record.amount)", subtitle: record.statusLabel, detail: record.createdAt)
|
||||
}
|
||||
|
||||
override func reloadContent() async {
|
||||
await viewModel.reload(api: services.walletAPI)
|
||||
updateFilterTitle()
|
||||
}
|
||||
|
||||
override func willDisplayTableRow(at indexPath: IndexPath) {
|
||||
guard indexPath.row >= viewModel.filteredRecords.count - 2 else { return }
|
||||
Task { await viewModel.loadMore(api: services.walletAPI) }
|
||||
}
|
||||
|
||||
@objc private func cycleFilter() {
|
||||
let filters = WithdrawalAuditFilter.allCases
|
||||
guard let index = filters.firstIndex(of: viewModel.selectedFilter) else { return }
|
||||
let next = filters[(index + 1) % filters.count]
|
||||
Task {
|
||||
viewModel.selectFilter(next)
|
||||
reloadTable()
|
||||
}
|
||||
}
|
||||
|
||||
private func updateFilterTitle() {
|
||||
navigationItem.rightBarButtonItem?.title = viewModel.selectedFilter.title
|
||||
}
|
||||
}
|
||||
|
||||
extension WithdrawalAuditViewModel: ViewModelBindable {}
|
||||
@ -0,0 +1,138 @@
|
||||
//
|
||||
// WithdrawalAuditViewModel.swift
|
||||
// suixinkan
|
||||
//
|
||||
// Created by Codex on 2026/6/25.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// 提现审核筛选项,按提现记录的状态文案聚合审核视角。
|
||||
enum WithdrawalAuditFilter: String, CaseIterable, Identifiable {
|
||||
case all
|
||||
case processing
|
||||
case completed
|
||||
|
||||
var id: String { rawValue }
|
||||
|
||||
/// 筛选项展示标题。
|
||||
var title: String {
|
||||
switch self {
|
||||
case .all: "全部"
|
||||
case .processing: "处理中"
|
||||
case .completed: "已完成"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
/// 提现审核 ViewModel,负责提现记录分页、状态筛选和失败状态清理。
|
||||
final class WithdrawalAuditViewModel {
|
||||
var onChange: (() -> Void)?
|
||||
var records: [WalletWithdrawRecord] = [] { didSet { onChange?() } }
|
||||
var selectedFilter: WithdrawalAuditFilter = .all { didSet { onChange?() } }
|
||||
var isLoading = false { didSet { onChange?() } }
|
||||
var isLoadingMore = false { didSet { onChange?() } }
|
||||
var loadFailed = false { didSet { onChange?() } }
|
||||
var loadFailureReason: String? { didSet { onChange?() } }
|
||||
var errorMessage: String? { didSet { onChange?() } }
|
||||
|
||||
private let pageSize = 10
|
||||
private var page = 1
|
||||
private var total = 0
|
||||
|
||||
/// 当前筛选下的提现记录。
|
||||
var filteredRecords: [WalletWithdrawRecord] {
|
||||
filteredRecords(for: selectedFilter)
|
||||
}
|
||||
|
||||
/// 是否还有下一页。
|
||||
var hasMore: Bool {
|
||||
records.count < total
|
||||
}
|
||||
|
||||
/// 记录总数,优先使用服务端 total。
|
||||
var totalCount: Int {
|
||||
max(total, records.count)
|
||||
}
|
||||
|
||||
/// 处理中数量。
|
||||
var processingCount: Int {
|
||||
filteredRecords(for: .processing).count
|
||||
}
|
||||
|
||||
/// 已完成数量。
|
||||
var completedCount: Int {
|
||||
filteredRecords(for: .completed).count
|
||||
}
|
||||
|
||||
/// 按指定筛选返回提现记录。
|
||||
func filteredRecords(for filter: WithdrawalAuditFilter) -> [WalletWithdrawRecord] {
|
||||
switch filter {
|
||||
case .all:
|
||||
return records
|
||||
case .processing:
|
||||
return records.filter { record in
|
||||
let status = record.statusLabel
|
||||
return status.contains("中") || status.contains("待")
|
||||
}
|
||||
case .completed:
|
||||
return records.filter { record in
|
||||
let status = record.statusLabel
|
||||
return status.contains("完成") || status.contains("到账") || status.contains("通过")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 切换筛选项,仅影响本地展示。
|
||||
func selectFilter(_ filter: WithdrawalAuditFilter) {
|
||||
selectedFilter = filter
|
||||
}
|
||||
|
||||
/// 重新加载第一页提现审核记录。
|
||||
func reload(api: WalletServing) async {
|
||||
isLoading = true
|
||||
loadFailed = false
|
||||
loadFailureReason = nil
|
||||
errorMessage = nil
|
||||
defer { isLoading = false }
|
||||
|
||||
do {
|
||||
let response = try await api.walletWithdrawList(page: 1, pageSize: pageSize)
|
||||
records = response.item
|
||||
total = response.total
|
||||
page = 1
|
||||
} catch {
|
||||
clearRecords()
|
||||
loadFailed = true
|
||||
loadFailureReason = error.localizedDescription
|
||||
errorMessage = error.localizedDescription
|
||||
}
|
||||
}
|
||||
|
||||
/// 加载下一页提现审核记录。
|
||||
func loadMore(api: WalletServing) async {
|
||||
guard hasMore, !isLoadingMore, !isLoading else { return }
|
||||
isLoadingMore = true
|
||||
errorMessage = nil
|
||||
defer { isLoadingMore = false }
|
||||
|
||||
let nextPage = page + 1
|
||||
do {
|
||||
let response = try await api.walletWithdrawList(page: nextPage, pageSize: pageSize)
|
||||
records += response.item
|
||||
total = response.total
|
||||
page = nextPage
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
}
|
||||
}
|
||||
|
||||
/// 清空旧提现审核数据,避免失败后残留上一账号或上一状态记录。
|
||||
private func clearRecords() {
|
||||
records = []
|
||||
page = 1
|
||||
total = 0
|
||||
isLoadingMore = false
|
||||
}
|
||||
}
|
||||
16
suixinkan_ios/Features/WithdrawalAudit/WithdrawalAudit.md
Normal file
16
suixinkan_ios/Features/WithdrawalAudit/WithdrawalAudit.md
Normal file
@ -0,0 +1,16 @@
|
||||
# 提现审核模块
|
||||
|
||||
## 模块职责
|
||||
|
||||
`Features/WithdrawalAudit` 承接首页 `withdrawal_audit` 权限入口,以审核视角展示钱包提现记录、状态筛选和单笔处理进度。
|
||||
|
||||
## 业务流程
|
||||
|
||||
- `WithdrawalAuditView` 进入后调用 `WalletAPI.walletWithdrawList` 加载第一页提现记录。
|
||||
- `WithdrawalAuditViewModel` 管理分页、加载更多、筛选状态和失败清理。
|
||||
- 筛选只在本地执行:处理中匹配状态文案中的“中/待”,已完成匹配“完成/到账/通过”。
|
||||
- 刷新失败会清空旧记录和分页状态,避免账号或权限切换后残留上一组财务数据。
|
||||
|
||||
## 边界
|
||||
|
||||
本模块不做管理员提现审批操作。旧 Android 工程未找到提现审批列表或操作接口,当前按旧 iOS 的提现记录审核视角迁移。
|
||||
Reference in New Issue
Block a user