jsdw_ios/QuickLocation/Section/Schedule/CreateSchedule/CreateScheduleVM.swift

181 lines
5.8 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// CreateScheduleVM.swift
// QuickLocation
//
// Created by 八条 on 2026/6/24.
//
import RxSwift
import RxCocoa
import RxDataSources
import SwiftDate
// MARK: - 行程点编辑模型
struct SchedulePointItem: IdentifiableType, Equatable {
let identity: String = UUID().uuidString
var locationName: String = ""
var address: String = ""
var latitude: Double = 0
var longitude: Double = 0
var province: String = ""
var city: String = ""
var district: String = ""
var street: String = ""
var country: String = ""
var formatted_address: String = ""
var expectedTime: Date?
var remark: String = ""
}
enum SchedulePointRole: Equatable {
case start
case middle
case end
}
enum CreateScheduleListItem: IdentifiableType, Equatable {
case point(index: Int, item: SchedulePointItem, role: SchedulePointRole)
case add
var identity: String {
switch self {
case .point(_, let item, _):
return item.identity
case .add:
return "__add__"
}
}
}
typealias SchedulePointSection = SectionModel<String, CreateScheduleListItem>
class CreateScheduleVM {
let disposeBag = DisposeBag()
let scheduModel: ScheduleModel?
// MARK: - Input
let addPointTapped = PublishSubject<Void>()
let deletePointAt = PublishSubject<Int>()
let movePoint = PublishSubject<(from: Int, to: Int)>()
let updatePoint = PublishSubject<(index: Int, item: SchedulePointItem)>()
let selectedDate = BehaviorRelay<Date>(value: Date())
var selectedGroupKeys: [String] = []
/// 切换圈子选中状态(已选则删除,未选则添加)
func toggleGroupKey(_ key: String) {
if let idx = selectedGroupKeys.firstIndex(of: key) {
selectedGroupKeys.remove(at: idx)
} else {
selectedGroupKeys.append(key)
}
}
/// 删除指定圈子 key
func removeGroupKey(_ key: String) {
selectedGroupKeys.removeAll { $0 == key }
}
// MARK: - Output
let pointsRelay: BehaviorRelay<[SchedulePointItem]>
let listItems: Observable<[CreateScheduleListItem]>
let dateString: Observable<String>
init(_ model: ScheduleModel?) {
self.scheduModel = model
if let m = model, !m.points.isEmpty {
var items = m.points.map { $0.toSchedulePointItem() }
while items.count < 2 {
items.append(SchedulePointItem())
}
pointsRelay = BehaviorRelay<[SchedulePointItem]>(value: items)
selectedDate.accept(Date(timeIntervalSince1970: TimeInterval(m.timestamp) / 1000))
selectedGroupKeys = m.groups.map { $0.group_key }
} else {
pointsRelay = BehaviorRelay<[SchedulePointItem]>(value: [SchedulePointItem(), SchedulePointItem()])
}
dateString = selectedDate
.map { $0.toFormat("yyyy年MM月dd日") }
listItems = pointsRelay.map { Self.buildListItems(from: $0) }
// 添加行程点:插入到终点之前
addPointTapped
.withLatestFrom(pointsRelay)
.map { list in
var new = list
let insertAt = max(new.count - 1, 1)
new.insert(SchedulePointItem(), at: insertAt)
return new
}
.bind(to: pointsRelay)
.disposed(by: disposeBag)
// 删除行程点:仅允许删中间点,且始终保留始+终
deletePointAt
.withLatestFrom(pointsRelay) { ($1, $0) }
.subscribe(onNext: { [weak self] list, idx in
guard list.count > 2, idx > 0, idx < list.count - 1 else { return }
var new = list
new.remove(at: idx)
self?.pointsRelay.accept(new)
})
.disposed(by: disposeBag)
// 中间点排序(始/终固定)
movePoint
.withLatestFrom(pointsRelay) { ($1, $0) }
.subscribe(onNext: { [weak self] list, move in
guard list.count > 2 else { return }
let from = move.from
let to = move.to
let middleRange = 1..<(list.count - 1)
guard middleRange.contains(from), middleRange.contains(to), from != to else { return }
var new = list
let item = new.remove(at: from)
new.insert(item, at: to)
self?.pointsRelay.accept(new)
})
.disposed(by: disposeBag)
// 更新某个行程点
updatePoint
.withLatestFrom(pointsRelay) { ($1, $0) }
.subscribe(onNext: { [weak self] list, update in
guard update.index < list.count else { return }
var new = list
new[update.index] = update.item
self?.pointsRelay.accept(new)
})
.disposed(by: disposeBag)
}
static func buildListItems(from points: [SchedulePointItem]) -> [CreateScheduleListItem] {
guard points.count >= 2 else {
return points.enumerated().map { idx, item in
let role: SchedulePointRole = idx == 0 ? .start : .end
return .point(index: idx, item: item, role: role)
}
}
var rows: [CreateScheduleListItem] = []
rows.append(.point(index: 0, item: points[0], role: .start))
if points.count > 2 {
for i in 1..<(points.count - 1) {
rows.append(.point(index: i, item: points[i], role: .middle))
}
}
rows.append(.add)
rows.append(.point(index: points.count - 1, item: points[points.count - 1], role: .end))
return rows
}
static func role(at index: Int, total: Int) -> SchedulePointRole {
if index == 0 { return .start }
if index == total - 1 { return .end }
return .middle
}
}