Extract shared LocationReportService, wire location_report menu routing, and add unit tests for reporting cooldown and history pagination. Co-authored-by: Cursor <cursoragent@cursor.com>
125 lines
3.8 KiB
Swift
125 lines
3.8 KiB
Swift
//
|
||
// LocationReportHistoryViewModel.swift
|
||
// suixinkan
|
||
//
|
||
|
||
import Foundation
|
||
|
||
/// 位置上报历史 ViewModel,对齐 Android `LocationReportHistoryViewModel`。
|
||
final class LocationReportHistoryViewModel {
|
||
|
||
private(set) var reportList: [LocationReportHistoryItem] = []
|
||
private(set) var selectedFilter: LocationReportFilterType = .all
|
||
private(set) var isRefreshing = false
|
||
private(set) var canLoadMore = false
|
||
private(set) var startDate: Date?
|
||
private(set) var endDate: Date?
|
||
|
||
var onStateChange: (() -> Void)?
|
||
var onShowMessage: ((String) -> Void)?
|
||
|
||
private let appStore: AppStore
|
||
private var currentPage = 1
|
||
private let pageSize = 10
|
||
private var isLoading = false
|
||
|
||
init(appStore: AppStore = .shared) {
|
||
self.appStore = appStore
|
||
}
|
||
|
||
var startDateText: String { formattedDate(startDate) }
|
||
var endDateText: String { formattedDate(endDate) }
|
||
var hasDateFilter: Bool { startDate != nil || endDate != nil }
|
||
|
||
/// 加载历史上报列表。
|
||
func loadReportList(api: HomeAPI, refresh: Bool = false) async {
|
||
guard !isLoading || refresh else { return }
|
||
let staffId = appStore.userId.trimmingCharacters(in: .whitespacesAndNewlines)
|
||
guard !staffId.isEmpty else {
|
||
onShowMessage?("获取用户ID失败")
|
||
return
|
||
}
|
||
|
||
if refresh {
|
||
currentPage = 1
|
||
isRefreshing = true
|
||
notifyStateChange()
|
||
}
|
||
isLoading = true
|
||
defer {
|
||
isLoading = false
|
||
isRefreshing = false
|
||
notifyStateChange()
|
||
}
|
||
|
||
do {
|
||
let response = try await api.locationReportList(
|
||
staffId: staffId,
|
||
page: currentPage,
|
||
pageSize: pageSize,
|
||
type: selectedFilter.typeCode,
|
||
startDate: apiDateString(startDate),
|
||
endDate: apiDateString(endDate)
|
||
)
|
||
if refresh || currentPage == 1 {
|
||
reportList = response.list
|
||
} else {
|
||
reportList.append(contentsOf: response.list)
|
||
}
|
||
currentPage += 1
|
||
canLoadMore = reportList.count < response.total
|
||
notifyStateChange()
|
||
} catch is CancellationError {
|
||
return
|
||
} catch {
|
||
onShowMessage?(error.localizedDescription)
|
||
}
|
||
}
|
||
|
||
func selectFilter(_ filter: LocationReportFilterType, api: HomeAPI) async {
|
||
selectedFilter = filter
|
||
currentPage = 1
|
||
await loadReportList(api: api, refresh: true)
|
||
}
|
||
|
||
func setDateRange(start: Date?, end: Date?, api: HomeAPI) async {
|
||
startDate = start
|
||
endDate = end
|
||
currentPage = 1
|
||
await loadReportList(api: api, refresh: true)
|
||
}
|
||
|
||
func clearDateRange(api: HomeAPI) async {
|
||
startDate = nil
|
||
endDate = nil
|
||
currentPage = 1
|
||
await loadReportList(api: api, refresh: true)
|
||
}
|
||
|
||
func loadMoreIfNeeded(currentIndex: Int, api: HomeAPI) async {
|
||
guard canLoadMore, !isLoading else { return }
|
||
guard currentIndex >= reportList.count - 2 else { return }
|
||
await loadReportList(api: api, refresh: false)
|
||
}
|
||
|
||
private func apiDateString(_ date: Date?) -> String? {
|
||
guard let date else { return nil }
|
||
let formatter = DateFormatter()
|
||
formatter.locale = Locale(identifier: "zh_CN")
|
||
formatter.dateFormat = "yyyy-MM-dd"
|
||
return formatter.string(from: date)
|
||
}
|
||
|
||
private func formattedDate(_ date: Date?) -> String {
|
||
guard let date else { return "请选择" }
|
||
let formatter = DateFormatter()
|
||
formatter.locale = Locale(identifier: "zh_CN")
|
||
formatter.dateFormat = "yyyy-MM-dd"
|
||
return formatter.string(from: date)
|
||
}
|
||
|
||
private func notifyStateChange() {
|
||
onStateChange?()
|
||
}
|
||
}
|