添加位置上报地图页和历史列表并对齐 Android

This commit is contained in:
2026-07-07 11:34:54 +08:00
parent 3c25a0b789
commit 3acbf6315b
17 changed files with 1975 additions and 152 deletions

View File

@ -0,0 +1,77 @@
//
// LocationReportHistoryViewModelTests.swift
// suixinkanTests
//
import XCTest
@testable import suixinkan
@MainActor
/// ViewModel
final class LocationReportHistoryViewModelTests: XCTestCase {
private var appStore: AppStore!
private var defaults: UserDefaults!
override func setUp() {
defaults = UserDefaults(suiteName: "LocationReportHistoryViewModelTests")!
defaults.removePersistentDomain(forName: "LocationReportHistoryViewModelTests")
appStore = AppStore(defaults: defaults)
appStore.userId = "staff-99"
}
override func tearDown() {
appStore.logout()
super.tearDown()
}
func testLoadReportListPopulatesItems() async throws {
let listJSON = """
{"code":100000,"msg":"success","data":{"list":[{"id":1,"staff_id":99,"type":1,"latitude":"30.1","longitude":"120.2","address":"","remark":"","created_at":"2026-01-01 10:00:00"}],"total":1}}
""".data(using: .utf8)!
let api = HomeAPI(client: APIClient(environment: .testing, session: MockURLSession(responses: [listJSON])))
let viewModel = LocationReportHistoryViewModel(appStore: appStore)
await viewModel.loadReportList(api: api, refresh: true)
XCTAssertEqual(viewModel.reportList.count, 1)
XCTAssertEqual(viewModel.reportList.first?.address, "测试地址")
XCTAssertFalse(viewModel.canLoadMore)
}
func testSelectFilterReloadsWithTypeCode() async throws {
let allJSON = """
{"code":100000,"msg":"success","data":{"list":[{"id":1,"staff_id":99,"type":1,"latitude":"30","longitude":"120","address":"A","remark":"","created_at":"2026-01-01 10:00:00"}],"total":1}}
""".data(using: .utf8)!
let markedJSON = """
{"code":100000,"msg":"success","data":{"list":[{"id":2,"staff_id":99,"type":2,"latitude":"31","longitude":"121","address":"B","remark":"","created_at":"2026-01-02 10:00:00"}],"total":1}}
""".data(using: .utf8)!
let api = HomeAPI(client: APIClient(environment: .testing, session: MockURLSession(responses: [allJSON, markedJSON])))
let viewModel = LocationReportHistoryViewModel(appStore: appStore)
await viewModel.loadReportList(api: api, refresh: true)
await viewModel.selectFilter(.marked, api: api)
XCTAssertEqual(viewModel.selectedFilter, .marked)
XCTAssertEqual(viewModel.reportList.first?.type, 2)
XCTAssertEqual(viewModel.reportList.first?.address, "B")
}
func testPaginationAppendsItems() async throws {
let page1JSON = """
{"code":100000,"msg":"success","data":{"list":[{"id":1,"staff_id":99,"type":1,"latitude":"30","longitude":"120","address":"","remark":"","created_at":"2026-01-01 10:00:00"}],"total":2}}
""".data(using: .utf8)!
let page2JSON = """
{"code":100000,"msg":"success","data":{"list":[{"id":2,"staff_id":99,"type":1,"latitude":"31","longitude":"121","address":"","remark":"","created_at":"2026-01-02 10:00:00"}],"total":2}}
""".data(using: .utf8)!
let api = HomeAPI(client: APIClient(environment: .testing, session: MockURLSession(responses: [page1JSON, page2JSON])))
let viewModel = LocationReportHistoryViewModel(appStore: appStore)
await viewModel.loadReportList(api: api, refresh: true)
await viewModel.loadMoreIfNeeded(currentIndex: 0, api: api)
XCTAssertEqual(viewModel.reportList.count, 2)
XCTAssertEqual(viewModel.reportList.last?.address, "第二页")
XCTAssertFalse(viewModel.canLoadMore)
}
}

View File

@ -0,0 +1,98 @@
//
// LocationReportServiceTests.swift
// suixinkanTests
//
import XCTest
@testable import suixinkan
@MainActor
///
final class LocationReportServiceTests: XCTestCase {
private var appStore: AppStore!
private var defaults: UserDefaults!
override func setUp() {
defaults = UserDefaults(suiteName: "LocationReportServiceTests")!
defaults.removePersistentDomain(forName: "LocationReportServiceTests")
appStore = AppStore(defaults: defaults)
appStore.userId = "staff-1"
appStore.currentScenicId = 100
}
override func tearDown() {
appStore.logout()
super.tearDown()
}
func testReportWithCoordinatesType2UsesProvidedLocation() async throws {
let reportJSON = """
{"code":100000,"msg":"success","data":{"staff_id":"staff-1","expired":0,"status":1}}
""".data(using: .utf8)!
let api = HomeAPI(client: APIClient(environment: .testing, session: MockURLSession(responses: [reportJSON])))
let stateStore = HomeLocationStateStore(store: appStore)
let service = LocationReportService(appStore: appStore, locationStateStore: stateStore)
let result = try await service.reportWithCoordinates(
api: api,
latitude: 30.1,
longitude: 120.2,
address: "标记地址",
type: 2
)
XCTAssertTrue(result.shouldShowSuccessDialog)
XCTAssertEqual(result.locationInfoText, "标记地址")
XCTAssertTrue(stateStore.isOnline)
}
func testTooFrequentManualReportThrows() async throws {
let reportJSON = """
{"code":100000,"msg":"success","data":{"staff_id":"staff-1","expired":0,"status":1}}
""".data(using: .utf8)!
let api = HomeAPI(client: APIClient(environment: .testing, session: MockURLSession(responses: [reportJSON, reportJSON])))
let stateStore = HomeLocationStateStore(store: appStore)
let service = LocationReportService(appStore: appStore, locationStateStore: stateStore)
_ = try await service.reportWithCoordinates(
api: api,
latitude: 30.0,
longitude: 120.0,
address: "测试地址",
type: 1
)
do {
_ = try await service.reportWithCoordinates(
api: api,
latitude: 30.0,
longitude: 120.0,
address: "测试地址",
type: 1
)
XCTFail("Expected too frequent error")
} catch let error as LocationReportServiceError {
if case .tooFrequent = error {
XCTAssertTrue(true)
} else {
XCTFail("Unexpected error \(error)")
}
}
}
func testMissingScenicThrows() async {
appStore.currentScenicId = 0
let api = HomeAPI(client: APIClient(environment: .testing, session: MockURLSession(responses: [])))
let stateStore = HomeLocationStateStore(store: appStore)
let service = LocationReportService(appStore: appStore, locationStateStore: stateStore)
do {
_ = try await service.manualReport(api: api)
XCTFail("Expected missing scenic")
} catch let error as LocationReportServiceError {
XCTAssertEqual(error, .missingScenic)
} catch {
XCTFail("Unexpected error \(error)")
}
}
}