// // 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 class CreateScheduleVM { let disposeBag = DisposeBag() let scheduModel: ScheduleModel? // MARK: - Input let addPointTapped = PublishSubject() let deletePointAt = PublishSubject() let movePoint = PublishSubject<(from: Int, to: Int)>() let updatePoint = PublishSubject<(index: Int, item: SchedulePointItem)>() let selectedDate = BehaviorRelay(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 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 } }