接入首页位置上报与在线倒计时,并优化上报交互体验。

持久化服务端 expired 过期时间戳以正确恢复倒计时,切换在线/离线与上报时展示定位 Loading,移除重复的成功 Alert,并将 Toast 调整为居中半透明样式。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-29 15:59:59 +08:00
parent d9f897038d
commit 560b52fcc8
19 changed files with 1028 additions and 204 deletions

View File

@ -8,28 +8,16 @@
import Foundation
import Combine
/// ViewModel线
/// ViewModel
@MainActor
final class LocationReportViewModel: ObservableObject {
@Published var isOnline = false
@Published var reminderMinutes = 30
@Published var secondsUntilReport = 0
@Published var currentCoordinate: LocationCoordinate?
@Published var markedCoordinate: LocationCoordinate?
@Published var currentAddress = ""
@Published var markedAddress = ""
@Published var lastReportText = ""
@Published var errorMessage: String?
@Published var isSubmitting = false
///
var countdownText: String {
guard secondsUntilReport > 0 else { return "可立即上报" }
let hours = secondsUntilReport / 3600
let minutes = (secondsUntilReport % 3600) / 60
return "\(hours)小时\(minutes)分钟后可再次提醒"
}
///
func applyCurrentLocation(latitude: Double, longitude: Double, address: String) {
currentCoordinate = LocationCoordinate(latitude: latitude, longitude: longitude)
@ -42,24 +30,13 @@ final class LocationReportViewModel: ObservableObject {
markedAddress = address
}
/// 线线
func setOnline(
_ value: Bool,
staffId: Int?,
scenicId: Int?,
api: any LocationReportServing
) async -> Bool {
isOnline = value
guard value else { return true }
return await submit(type: .onlineStatus, staffId: staffId, scenicId: scenicId, api: api)
}
///
func submit(
type: LocationReportType,
staffId: Int?,
scenicId: Int?,
api: any LocationReportServing
homeLocationViewModel: HomeLocationViewModel,
toast: ToastCenter
) async -> Bool {
guard !isSubmitting else { return false }
guard let staffId, staffId > 0 else {
@ -86,28 +63,19 @@ final class LocationReportViewModel: ObservableObject {
errorMessage = nil
defer { isSubmitting = false }
do {
let response = try await api.reportLocation(
staffId: staffId,
latitude: coordinate.latitude,
longitude: coordinate.longitude,
address: address,
type: type,
scenicId: scenicId
)
secondsUntilReport = response.expired > 0 ? response.expired : 7200
lastReportText = LocationReportViewModel.displayFormatter.string(from: Date())
return true
} catch {
errorMessage = error.localizedDescription
return false
let success = await homeLocationViewModel.reportCoordinate(
latitude: coordinate.latitude,
longitude: coordinate.longitude,
address: address,
type: type,
staffId: staffId,
scenicId: scenicId,
toast: toast
)
if !success, errorMessage == nil {
errorMessage = "上报失败"
}
}
///
func tick() {
guard secondsUntilReport > 0 else { return }
secondsUntilReport -= 1
return success
}
///
@ -115,16 +83,10 @@ final class LocationReportViewModel: ObservableObject {
switch type {
case .marked:
(markedCoordinate ?? currentCoordinate, markedAddress.isEmpty ? currentAddress : markedAddress)
case .all, .immediate, .onlineStatus:
case .all, .immediate, .offlineReport:
(currentCoordinate, currentAddress)
}
}
private static let displayFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
return formatter
}()
}
/// ViewModel