同步已迁移的 iOS 模块
This commit is contained in:
196
suixinkan/Features/Orders/ViewModels/OrdersViewModel.swift
Normal file
196
suixinkan/Features/Orders/ViewModels/OrdersViewModel.swift
Normal file
@ -0,0 +1,196 @@
|
||||
//
|
||||
// OrdersViewModel.swift
|
||||
// suixinkan
|
||||
//
|
||||
// Created by Codex on 2026/6/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Observation
|
||||
|
||||
@MainActor
|
||||
@Observable
|
||||
/// 订单页面 ViewModel,管理订单列表、核销列表、筛选条件和手动核销流程。
|
||||
final class OrdersViewModel {
|
||||
var selectedEntry: OrdersEntry = .storeOrders
|
||||
var selectedStatus = 0
|
||||
var searchPhone = ""
|
||||
var filterStartDate: Date?
|
||||
var filterEndDate: Date?
|
||||
private(set) var loading = false
|
||||
private(set) var loadingMore = false
|
||||
private(set) var isVerifying = false
|
||||
private(set) var currentVerifyingOrderNumber: String?
|
||||
|
||||
private(set) var storeOrders: [OrderEntity] = []
|
||||
private(set) var storeTotal = 0
|
||||
private(set) var storePage = 1
|
||||
private(set) var storeHasMore = false
|
||||
|
||||
private(set) var writeOffOrders: [WriteOffOrderItem] = []
|
||||
private(set) var writeOffTotal = 0
|
||||
private(set) var writeOffPage = 1
|
||||
private(set) var writeOffHasMore = false
|
||||
|
||||
/// 返回去除首尾空白后的手机号搜索值。
|
||||
var normalizedPhone: String? {
|
||||
let text = searchPhone.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
return text.isEmpty ? nil : text
|
||||
}
|
||||
|
||||
/// 根据当前子入口刷新对应列表。
|
||||
func reload(api: OrderServing, scenicId: Int?, roleId: Int?, showLoading: Bool = true) async throws {
|
||||
switch selectedEntry {
|
||||
case .storeOrders:
|
||||
try await reloadStoreOrders(api: api, scenicId: scenicId, roleId: roleId, showLoading: showLoading)
|
||||
case .verificationOrders:
|
||||
try await reloadWriteOffOrders(api: api, scenicId: scenicId, showLoading: showLoading)
|
||||
}
|
||||
}
|
||||
|
||||
/// 刷新订单管理列表,缺少景区时清空旧数据。
|
||||
func reloadStoreOrders(api: OrderServing, scenicId: Int?, roleId: Int?, showLoading: Bool = true) async throws {
|
||||
guard let scenicId else {
|
||||
storeOrders = []
|
||||
storeTotal = 0
|
||||
storePage = 1
|
||||
storeHasMore = false
|
||||
return
|
||||
}
|
||||
|
||||
if showLoading { loading = true }
|
||||
defer { if showLoading { loading = false } }
|
||||
|
||||
let result = try await api.orderList(
|
||||
scenicId: scenicId,
|
||||
page: 1,
|
||||
pageSize: pageSize,
|
||||
orderStatus: orderStatusQuery,
|
||||
userPhone: normalizedPhone,
|
||||
startTime: startTimeQuery,
|
||||
endTime: endTimeQuery,
|
||||
isRefined: refinedQuery,
|
||||
isScenicAdmin: roleId == 53
|
||||
)
|
||||
storeOrders = result.list
|
||||
storeTotal = result.total
|
||||
storePage = 1
|
||||
storeHasMore = storeOrders.count < result.total && !result.list.isEmpty
|
||||
}
|
||||
|
||||
/// 加载订单管理下一页。
|
||||
func loadMoreStoreOrders(api: OrderServing, scenicId: Int?, roleId: Int?) async throws {
|
||||
guard !loadingMore, storeHasMore, let scenicId else { return }
|
||||
loadingMore = true
|
||||
defer { loadingMore = false }
|
||||
|
||||
let nextPage = storePage + 1
|
||||
let result = try await api.orderList(
|
||||
scenicId: scenicId,
|
||||
page: nextPage,
|
||||
pageSize: pageSize,
|
||||
orderStatus: orderStatusQuery,
|
||||
userPhone: normalizedPhone,
|
||||
startTime: startTimeQuery,
|
||||
endTime: endTimeQuery,
|
||||
isRefined: refinedQuery,
|
||||
isScenicAdmin: roleId == 53
|
||||
)
|
||||
storeOrders.append(contentsOf: result.list)
|
||||
storeTotal = result.total
|
||||
storePage = nextPage
|
||||
storeHasMore = storeOrders.count < result.total && !result.list.isEmpty
|
||||
}
|
||||
|
||||
/// 刷新核销订单列表,缺少景区时清空旧数据。
|
||||
func reloadWriteOffOrders(api: OrderServing, scenicId: Int?, showLoading: Bool = true) async throws {
|
||||
guard let scenicId else {
|
||||
writeOffOrders = []
|
||||
writeOffTotal = 0
|
||||
writeOffPage = 1
|
||||
writeOffHasMore = false
|
||||
return
|
||||
}
|
||||
|
||||
if showLoading { loading = true }
|
||||
defer { if showLoading { loading = false } }
|
||||
|
||||
let result = try await api.writeOffList(scenicId: scenicId, page: 1, pageSize: pageSize)
|
||||
writeOffOrders = result.list
|
||||
writeOffTotal = result.total
|
||||
writeOffPage = 1
|
||||
writeOffHasMore = writeOffOrders.count < result.total && !result.list.isEmpty
|
||||
}
|
||||
|
||||
/// 加载核销订单下一页。
|
||||
func loadMoreWriteOffOrders(api: OrderServing, scenicId: Int?) async throws {
|
||||
guard !loadingMore, writeOffHasMore, let scenicId else { return }
|
||||
loadingMore = true
|
||||
defer { loadingMore = false }
|
||||
|
||||
let nextPage = writeOffPage + 1
|
||||
let result = try await api.writeOffList(scenicId: scenicId, page: nextPage, pageSize: pageSize)
|
||||
writeOffOrders.append(contentsOf: result.list)
|
||||
writeOffTotal = result.total
|
||||
writeOffPage = nextPage
|
||||
writeOffHasMore = writeOffOrders.count < result.total && !result.list.isEmpty
|
||||
}
|
||||
|
||||
/// 手动核销订单,并在成功后刷新核销列表。
|
||||
func verify(api: OrderServing, scenicId: Int, orderNumber: String) async throws {
|
||||
guard !isVerifying else { return }
|
||||
|
||||
isVerifying = true
|
||||
currentVerifyingOrderNumber = orderNumber
|
||||
defer {
|
||||
isVerifying = false
|
||||
currentVerifyingOrderNumber = nil
|
||||
}
|
||||
|
||||
try await api.writeOff(orderNumber: orderNumber)
|
||||
let refreshed = try await api.writeOffList(scenicId: scenicId, page: 1, pageSize: pageSize)
|
||||
writeOffOrders = refreshed.list
|
||||
writeOffTotal = refreshed.total
|
||||
writeOffPage = 1
|
||||
writeOffHasMore = writeOffOrders.count < refreshed.total && !refreshed.list.isEmpty
|
||||
}
|
||||
|
||||
/// 用于测试重复提交场景的内部状态设置。
|
||||
func markVerifyingForTests(orderNumber: String) {
|
||||
isVerifying = true
|
||||
currentVerifyingOrderNumber = orderNumber
|
||||
}
|
||||
|
||||
private let pageSize = 20
|
||||
|
||||
private var orderStatusQuery: Int? {
|
||||
selectedStatus > 0 ? selectedStatus : nil
|
||||
}
|
||||
|
||||
private var startTimeQuery: String? {
|
||||
filterStartDate.map { Self.dayFormatter.string(from: $0) }
|
||||
}
|
||||
|
||||
private var endTimeQuery: String? {
|
||||
filterEndDate.map { Self.dayFormatter.string(from: $0) }
|
||||
}
|
||||
|
||||
private var refinedQuery: Int? {
|
||||
switch selectedStatus {
|
||||
case -1:
|
||||
return 1
|
||||
case -2:
|
||||
return 2
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private static let dayFormatter: DateFormatter = {
|
||||
let formatter = DateFormatter()
|
||||
formatter.calendar = Calendar(identifier: .gregorian)
|
||||
formatter.locale = Locale(identifier: "en_US_POSIX")
|
||||
formatter.dateFormat = "yyyy-MM-dd"
|
||||
return formatter
|
||||
}()
|
||||
}
|
||||
Reference in New Issue
Block a user