Initial commit: suixinkan_ios UIKit rewrite project.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-26 14:33:31 +08:00
commit 9edf993432
297 changed files with 47151 additions and 0 deletions

View File

@ -0,0 +1,166 @@
//
// TaskManagementViewModel.swift
// suixinkan
//
// Created by Codex on 2026/6/23.
//
import Foundation
///
enum TaskStatusFilter: Int, CaseIterable, Identifiable {
case all = 0
case unaccepted = 1
case accepted = 2
case completed = 3
case rejected = 4
var id: Int { rawValue }
///
var title: String {
switch self {
case .all:
"全部"
case .unaccepted:
"待接收"
case .accepted:
"处理中"
case .completed:
"已完成"
case .rejected:
"已驳回"
}
}
}
/// ViewModel
@MainActor
final class TaskManagementViewModel {
var onChange: (() -> Void)?
var isLoading = false { didSet { onChange?() } }
var isLoadingMore = false { didSet { onChange?() } }
var tasks: [PhotographerTaskItem] = [] { didSet { onChange?() } }
var total = 0 { didSet { onChange?() } }
var page = 1 { didSet { onChange?() } }
var selectedStatus: TaskStatusFilter = .all { didSet { onChange?() } }
var searchText = "" { didSet { onChange?() } }
var startDate: Date? { didSet { onChange?() } }
var endDate: Date? { didSet { onChange?() } }
var errorMessage: String? { didSet { onChange?() } }
private let pageSize = 10
///
var hasMore: Bool {
tasks.count < total
}
///
func reload(api: any TaskServing, scenicId: Int?) async throws {
guard let scenicId else {
resetList()
errorMessage = "请先选择景区"
return
}
isLoading = true
errorMessage = nil
defer { isLoading = false }
do {
let payload = try await requestList(api: api, scenicId: scenicId, page: 1)
page = 1
tasks = payload.list
total = payload.total
} catch {
resetList()
errorMessage = error.localizedDescription
throw error
}
}
///
func loadMore(api: any TaskServing, scenicId: Int?) async throws {
guard let scenicId, hasMore, !isLoadingMore else { return }
isLoadingMore = true
errorMessage = nil
defer { isLoadingMore = false }
let nextPage = page + 1
do {
let payload = try await requestList(api: api, scenicId: scenicId, page: nextPage)
page = nextPage
total = payload.total
tasks.append(contentsOf: payload.list)
} catch {
errorMessage = error.localizedDescription
throw error
}
}
///
func resetList() {
tasks = []
total = 0
page = 1
isLoading = false
isLoadingMore = false
}
///
private func requestList(api: any TaskServing, scenicId: Int, page: Int) async throws -> ListPayload<PhotographerTaskItem> {
try await api.taskList(
scenicId: scenicId,
page: page,
pageSize: pageSize,
taskStatus: selectedStatus.rawValue,
taskName: trimmedSearchText,
startTime: Self.requestDateString(from: startDate),
endTime: Self.requestDateString(from: endDate)
)
}
///
private var trimmedSearchText: String? {
let text = searchText.trimmingCharacters(in: .whitespacesAndNewlines)
return text.isEmpty ? nil : text
}
///
private static let requestDateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .gregorian)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "yyyy-MM-dd"
return formatter
}()
///
private static func requestDateString(from date: Date?) -> String? {
guard let date else { return nil }
return requestDateFormatter.string(from: date)
}
}
/// ViewModel
@MainActor
final class TaskDetailViewModel {
var onChange: (() -> Void)?
var isLoading = false { didSet { onChange?() } }
var detail: TaskDetailResponse? { didSet { onChange?() } }
var errorMessage: String? { didSet { onChange?() } }
///
func load(api: any TaskServing, taskId: Int) async {
isLoading = true
errorMessage = nil
defer { isLoading = false }
do {
detail = try await api.taskDetail(id: taskId)
} catch {
errorMessage = error.localizedDescription
}
}
}