142 lines
4.9 KiB
Swift
142 lines
4.9 KiB
Swift
//
|
||
// WriteOffListViewModel.swift
|
||
// suixinkan
|
||
//
|
||
// Created by Codex on 2026/6/29.
|
||
//
|
||
|
||
import Foundation
|
||
import Combine
|
||
|
||
@MainActor
|
||
/// 核销管理页 ViewModel,对齐 Android WriteOffListViewModel。
|
||
final class WriteOffListViewModel: ObservableObject {
|
||
@Published var searchPhone = ""
|
||
@Published var selectedStatus = 0
|
||
@Published var filterStartDate: Date?
|
||
@Published var filterEndDate: Date?
|
||
@Published private(set) var orders: [WriteOffOrderItem] = []
|
||
@Published private(set) var loading = false
|
||
@Published private(set) var loadingMore = false
|
||
@Published private(set) var isVerifying = false
|
||
@Published private(set) var total = 0
|
||
@Published private(set) var page = 1
|
||
@Published private(set) var hasMore = false
|
||
|
||
private let pageSize = 20
|
||
|
||
/// 返回去除首尾空白后的手机号搜索值。
|
||
var normalizedPhone: String? {
|
||
let text = searchPhone.trimmingCharacters(in: .whitespacesAndNewlines)
|
||
return text.isEmpty ? nil : text
|
||
}
|
||
|
||
/// 刷新核销列表。
|
||
func reload(api: OrderServing, scenicId: Int?, storeId: Int? = nil, showLoading: Bool = true) async throws {
|
||
guard let scenicId else {
|
||
orders = []
|
||
total = 0
|
||
page = 1
|
||
hasMore = false
|
||
return
|
||
}
|
||
|
||
if showLoading { loading = true }
|
||
defer { if showLoading { loading = false } }
|
||
|
||
let result = try await api.writeOffList(
|
||
scenicId: scenicId,
|
||
storeId: storeId,
|
||
page: 1,
|
||
pageSize: pageSize,
|
||
orderStatus: orderStatusQuery,
|
||
orderVerifyStatus: verifyStatusQuery,
|
||
startTime: OrderDateRangeFormatter.queryValue(from: filterStartDate),
|
||
endTime: OrderDateRangeFormatter.queryValue(from: filterEndDate),
|
||
userPhone: normalizedPhone
|
||
)
|
||
orders = result.list
|
||
total = result.total
|
||
page = 1
|
||
hasMore = orders.count < result.total && !result.list.isEmpty
|
||
}
|
||
|
||
/// 加载下一页。
|
||
func loadMore(api: OrderServing, scenicId: Int?, storeId: Int? = nil) async throws {
|
||
guard !loadingMore, hasMore, let scenicId else { return }
|
||
loadingMore = true
|
||
defer { loadingMore = false }
|
||
|
||
let nextPage = page + 1
|
||
let result = try await api.writeOffList(
|
||
scenicId: scenicId,
|
||
storeId: storeId,
|
||
page: nextPage,
|
||
pageSize: pageSize,
|
||
orderStatus: orderStatusQuery,
|
||
orderVerifyStatus: verifyStatusQuery,
|
||
startTime: OrderDateRangeFormatter.queryValue(from: filterStartDate),
|
||
endTime: OrderDateRangeFormatter.queryValue(from: filterEndDate),
|
||
userPhone: normalizedPhone
|
||
)
|
||
orders.append(contentsOf: result.list)
|
||
total = result.total
|
||
page = nextPage
|
||
hasMore = orders.count < result.total && !result.list.isEmpty
|
||
}
|
||
|
||
/// 核销订单并刷新列表。
|
||
func verify(api: OrderServing, scenicId: Int, storeId: Int? = nil, orderNumber: String) async throws {
|
||
guard !isVerifying else { return }
|
||
isVerifying = true
|
||
defer { isVerifying = false }
|
||
|
||
try await api.writeOff(orderNumber: orderNumber)
|
||
try await reload(api: api, scenicId: scenicId, storeId: storeId, showLoading: false)
|
||
}
|
||
|
||
/// 解析扫码结果并返回当前列表中的匹配订单。
|
||
func matchedWriteOffOrder(for rawCode: String) -> (orderNumber: String, matched: WriteOffOrderItem?)? {
|
||
guard let orderNumber = OrderNumberParser.parse(rawCode) else { return nil }
|
||
let matched = orders.first {
|
||
$0.orderNumber.caseInsensitiveCompare(orderNumber) == .orderedSame
|
||
}
|
||
return (matched?.orderNumber ?? orderNumber, matched)
|
||
}
|
||
|
||
/// 根据订单号查找当前列表中的订单。
|
||
func writeOffOrder(matching orderNumber: String) -> WriteOffOrderItem? {
|
||
orders.first { $0.orderNumber.caseInsensitiveCompare(orderNumber) == .orderedSame }
|
||
}
|
||
|
||
/// 判断订单是否仍可核销。
|
||
func canVerify(_ item: WriteOffOrderItem) -> Bool {
|
||
let status = displayVerificationStatus(for: item)
|
||
return !status.contains("已核销") && !status.contains("已退款") && !status.contains("已完成")
|
||
}
|
||
|
||
/// 返回核销状态展示文案。
|
||
func displayVerificationStatus(for item: WriteOffOrderItem) -> String {
|
||
let text = item.orderVerificationStatus.trimmingCharacters(in: .whitespacesAndNewlines)
|
||
return text.isEmpty ? "待核销" : text
|
||
}
|
||
|
||
private var orderStatusQuery: Int? {
|
||
switch selectedStatus {
|
||
case 18, 30:
|
||
return selectedStatus
|
||
default:
|
||
return nil
|
||
}
|
||
}
|
||
|
||
private var verifyStatusQuery: Int? {
|
||
switch selectedStatus {
|
||
case 1, 2:
|
||
return selectedStatus
|
||
default:
|
||
return nil
|
||
}
|
||
}
|
||
}
|