219 lines
7.1 KiB
Swift
219 lines
7.1 KiB
Swift
//
|
|
// InviteViewModels.swift
|
|
// suixinkan
|
|
//
|
|
|
|
import Foundation
|
|
import UIKit
|
|
|
|
/// 邀请页 ViewModel,负责加载邀请信息、生成二维码和暴露复制内容。
|
|
final class PhotographerInviteViewModel {
|
|
private(set) var isLoading = false
|
|
private(set) var inviteCode = ""
|
|
private(set) var inviteUrl = ""
|
|
private(set) var rules: [String] = []
|
|
private(set) var qrImage: UIImage?
|
|
private(set) var errorMessage: String?
|
|
|
|
var onStateChange: (() -> Void)?
|
|
|
|
/// 重新加载邀请信息。
|
|
func reload(api: any InviteServing) async {
|
|
isLoading = true
|
|
errorMessage = nil
|
|
notifyStateChange()
|
|
|
|
do {
|
|
let response = try await api.inviteInfo()
|
|
inviteCode = response.inviteCode
|
|
inviteUrl = response.inviteUrl
|
|
rules = response.description
|
|
qrImage = QRCodeGenerator.generateStyledQRCode(
|
|
content: response.inviteUrl,
|
|
pixelSize: 560,
|
|
backgroundColor: .white,
|
|
cornerRadius: 0
|
|
)
|
|
} catch {
|
|
clear()
|
|
errorMessage = error.localizedDescription
|
|
}
|
|
|
|
isLoading = false
|
|
notifyStateChange()
|
|
}
|
|
|
|
/// 返回待复制的邀请码。
|
|
func inviteCodeForCopy() -> String {
|
|
inviteCode
|
|
}
|
|
|
|
/// 返回待复制的邀请链接。
|
|
func inviteUrlForCopy() -> String {
|
|
inviteUrl
|
|
}
|
|
|
|
private func clear() {
|
|
inviteCode = ""
|
|
inviteUrl = ""
|
|
rules = []
|
|
qrImage = nil
|
|
}
|
|
|
|
private func notifyStateChange() {
|
|
onStateChange?()
|
|
}
|
|
}
|
|
|
|
/// 邀请记录 ViewModel,负责邀请用户、奖励流水和钱包汇总分页。
|
|
final class InviteRecordViewModel {
|
|
private(set) var selectedTab: InviteRecordTab = .invite
|
|
private(set) var totalRewardText = "0.00"
|
|
private(set) var withdrawableText = "0.00"
|
|
private(set) var inviteRows: [InviteDisplayRow] = []
|
|
private(set) var rewardRows: [WalletTransactionItem] = []
|
|
private(set) var isInviteRefreshing = false
|
|
private(set) var isRewardRefreshing = false
|
|
private(set) var canLoadMoreInvite = false
|
|
private(set) var canLoadMoreReward = false
|
|
private(set) var errorMessage: String?
|
|
|
|
var onStateChange: (() -> Void)?
|
|
|
|
private var invitePage = 1
|
|
private let invitePageSize = 20
|
|
private var rewardPage = 1
|
|
private let rewardPageSize = 10
|
|
private var isLoadingInvite = false
|
|
private var isLoadingReward = false
|
|
|
|
/// 初次加载邀请记录和钱包汇总。
|
|
func loadInitial(inviteAPI: any InviteServing, walletAPI: any WalletServing) async {
|
|
await refreshInvite(inviteAPI: inviteAPI, walletAPI: walletAPI)
|
|
}
|
|
|
|
/// 切换分段并在需要时加载奖励记录。
|
|
func selectTab(_ tab: InviteRecordTab, inviteAPI: any InviteServing, walletAPI: any WalletServing) async {
|
|
guard tab != selectedTab else { return }
|
|
selectedTab = tab
|
|
notifyStateChange()
|
|
if tab == .reward, rewardRows.isEmpty {
|
|
await refreshReward(walletAPI: walletAPI)
|
|
}
|
|
}
|
|
|
|
/// 刷新邀请记录和钱包汇总。
|
|
func refreshInvite(inviteAPI: any InviteServing, walletAPI: any WalletServing) async {
|
|
await loadSummary(walletAPI: walletAPI)
|
|
await loadInviteRows(inviteAPI: inviteAPI, refresh: true)
|
|
}
|
|
|
|
/// 加载下一页邀请记录。
|
|
func loadMoreInvite(inviteAPI: any InviteServing) async {
|
|
guard canLoadMoreInvite, !isInviteRefreshing else { return }
|
|
await loadInviteRows(inviteAPI: inviteAPI, refresh: false)
|
|
}
|
|
|
|
/// 刷新奖励记录和钱包汇总。
|
|
func refreshReward(walletAPI: any WalletServing) async {
|
|
await loadSummary(walletAPI: walletAPI)
|
|
await loadRewardRows(walletAPI: walletAPI, refresh: true)
|
|
}
|
|
|
|
/// 加载下一页奖励记录。
|
|
func loadMoreReward(walletAPI: any WalletServing) async {
|
|
guard canLoadMoreReward, !isRewardRefreshing else { return }
|
|
await loadRewardRows(walletAPI: walletAPI, refresh: false)
|
|
}
|
|
|
|
private func loadSummary(walletAPI: any WalletServing) async {
|
|
do {
|
|
let summary = try await walletAPI.walletSummary(type: 2)
|
|
totalRewardText = summary.amountTotal.isEmpty ? "0.00" : summary.amountTotal
|
|
withdrawableText = summary.amountWithdrawable.isEmpty ? "0.00" : summary.amountWithdrawable
|
|
} catch {
|
|
errorMessage = error.localizedDescription
|
|
}
|
|
notifyStateChange()
|
|
}
|
|
|
|
private func loadInviteRows(inviteAPI: any InviteServing, refresh: Bool) async {
|
|
if isLoadingInvite && !refresh { return }
|
|
if refresh { invitePage = 1 }
|
|
isLoadingInvite = true
|
|
isInviteRefreshing = refresh
|
|
errorMessage = nil
|
|
notifyStateChange()
|
|
|
|
do {
|
|
let users = try await inviteAPI.inviteUserList(page: invitePage, pageSize: invitePageSize)
|
|
let mapped = users.map { user in
|
|
InviteDisplayRow(
|
|
id: "invite_\(user.id)",
|
|
title: user.realName.isEmpty ? "**\(user.phone.suffix(4))" : user.realName,
|
|
phone: user.phone,
|
|
avatar: user.avatar,
|
|
time: user.createdAt,
|
|
inviteLevel: user.inviteLevel
|
|
)
|
|
}
|
|
inviteRows = refresh ? mapped : inviteRows + mapped
|
|
invitePage += 1
|
|
canLoadMoreInvite = users.count >= invitePageSize
|
|
} catch {
|
|
if refresh {
|
|
inviteRows = []
|
|
canLoadMoreInvite = false
|
|
}
|
|
errorMessage = error.localizedDescription
|
|
}
|
|
|
|
isLoadingInvite = false
|
|
isInviteRefreshing = false
|
|
notifyStateChange()
|
|
}
|
|
|
|
private func loadRewardRows(walletAPI: any WalletServing, refresh: Bool) async {
|
|
if isLoadingReward && !refresh { return }
|
|
if refresh { rewardPage = 1 }
|
|
isLoadingReward = true
|
|
isRewardRefreshing = refresh
|
|
errorMessage = nil
|
|
notifyStateChange()
|
|
|
|
do {
|
|
let response = try await walletAPI.walletTransactionList(
|
|
startDate: "2025-01-01",
|
|
endDate: Self.dayFormatter.string(from: Date()),
|
|
page: rewardPage,
|
|
pageSize: rewardPageSize,
|
|
type: 2
|
|
)
|
|
rewardRows = refresh ? response.list : rewardRows + response.list
|
|
rewardPage += 1
|
|
canLoadMoreReward = response.list.count >= rewardPageSize && rewardRows.count < response.total
|
|
} catch {
|
|
if refresh {
|
|
rewardRows = []
|
|
canLoadMoreReward = false
|
|
}
|
|
errorMessage = error.localizedDescription
|
|
}
|
|
|
|
isLoadingReward = false
|
|
isRewardRefreshing = false
|
|
notifyStateChange()
|
|
}
|
|
|
|
private func notifyStateChange() {
|
|
onStateChange?()
|
|
}
|
|
|
|
private static let dayFormatter: DateFormatter = {
|
|
let formatter = DateFormatter()
|
|
formatter.locale = Locale(identifier: "zh_CN")
|
|
formatter.dateFormat = "yyyy-MM-dd"
|
|
return formatter
|
|
}()
|
|
}
|