// // GroupMemberListVC.swift // QuickLocation // // Created by 八条 on 2026/6/29. // import UIKit import RxSwift import RxCocoa import RxDataSources import ObjectMapper class GroupMemberListVC: BaseViewController { fileprivate var rootView: GroupMemberListView! override func loadView() { rootView = GroupMemberListView(frame: UIScreen.main.bounds) view = rootView } private var viewModel: GroupMemberListVM override func viewDidLoad() { super.viewDidLoad() bindViewModel() reactiveAction() requestGroupInfo() guard let systemConfig = AppContextManager.shared.systemConfig, systemConfig.globalDisplay else { return } rootView.unlockVipMaskView.isHidden = true if AppContextManager.shared.vip == 1 { rootView.tipsImg.image = UIImage(named: "GroupMemberList/mask_tips") rootView.unlockVipMaskView.isHidden = false } NotificationCenter.default.rx.notification(.RequestOrderPayStatusNotification) .subscribe(onNext: { [weak self] _ in guard let self = self else { return } self.rootView.unlockVipMaskView.isHidden = true if AppContextManager.shared.vip == 1 { self.rootView.tipsImg.image = UIImage(named: "GroupMemberList/mask_tips") self.rootView.unlockVipMaskView.isHidden = false } else { self.rootView.refreshVipState() } }).disposed(by: disposeBag) } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // rootView.selectedDate.accept(Date()) // rootView.scrollToToday() } private func reactiveAction() { } private func bindViewModel() { viewModel.output.sectionedItems .bind(to: rootView.collectionView.rx.items(dataSource: memberDataSource)) .disposed(by: disposeBag) viewModel.output.drivingSectionedItems .bind(to: rootView.drivingEventCV.rx.items(dataSource: drivingDataSource)) .disposed(by: disposeBag) viewModel.output.scheduleSectionedItems .bind(to: rootView.tableView.rx.items(dataSource: scheduleDataSource)) .disposed(by: disposeBag) viewModel.output.scheduleSectionedItems .subscribe(onNext: { [weak self] items in self?.rootView.updateTableViewHeight(count: items.first?.items.count ?? 0) }).disposed(by: disposeBag) rootView.tableView.rx.modelSelected(ScheduleRecordModel.self) .subscribe(viewModel.scheduleCellRecordAction.inputs) .disposed(by: disposeBag) // 日期选择 → 滚动报告到顶部 + 请求驾驶数据 rootView.selectedDate .skip(1) .subscribe(onNext: { [weak self] date in guard let self = self else { return } self.rootView.scrollReportToTop() guard !self.rootView.isSelectedDateLocked else { return } self.requestDrivingEvents(for: date) }).disposed(by: disposeBag) // 成员点击 → 重置日期到当天(selectedDate 发射 → 自动触发 API 请求) rootView.collectionView.rx.modelSelected(GroupMemberModel.self) .subscribe(onNext: { [weak self] model in guard let self = self else { return } self.viewModel.memberId = model.user_id self.rootView.selectedMemberIsSelf = self.viewModel.isCurrentUser(id: model.user_id) self.rootView.unlockVipMaskView.isHidden = true self.rootView.selectedDate.accept(Date()) self.rootView.scrollToToday() self.rootView.scrollReportToTop() self.rootView.collectionView.reloadData() }).disposed(by: disposeBag) } /// 根据日期生成起止时间戳并请求驾驶数据 private func requestDrivingEvents(for date: Date) { guard !viewModel.memberId.isEmpty else { return } let calendar = Calendar.current let start = calendar.startOfDay(for: date) let end = calendar.date(bySettingHour: 23, minute: 59, second: 59, of: date) ?? date // let end: Date // if calendar.isDateInToday(date) { // end = Date() // 当天以当前时间为结束 // } else { // end = calendar.date(bySettingHour: 23, minute: 59, second: 59, of: date) ?? date // } requestDrivingEvents(start_time: "\(Int64(start.timeIntervalSince1970 * 1000))", end_time: "\(Int64(end.timeIntervalSince1970 * 1000))") requestPlayback(date: "\(Int64(start.timeIntervalSince1970 * 1000))") } // MARK: - dataSource private lazy var scheduleDataSource: RxTableViewSectionedReloadDataSource = { RxTableViewSectionedReloadDataSource { _, tableView, indexPath, model in let cell: ScheduleRecordCell = tableView.dequeueReusableCell(for: indexPath) cell.configure(model) return cell } }() private lazy var memberDataSource: RxCollectionViewSectionedReloadDataSource = { RxCollectionViewSectionedReloadDataSource { [weak self] datasource, collectionView, indexPath, model in let cell: GroupMemberListCell = collectionView.dequeueReusableCell(for: indexPath) cell.configure(model: model, isCurrentUser: self?.viewModel.isCurrentUser(id: model.user_id) ?? false, isSelected: model.user_id == self?.viewModel.memberId) return cell } }() private lazy var drivingDataSource: RxCollectionViewSectionedReloadDataSource = { RxCollectionViewSectionedReloadDataSource { _, collectionView, indexPath, item in let cell: DrivingEventCell = collectionView.dequeueReusableCell(for: indexPath) cell.configure(item) return cell } }() // MARK: - API private func requestGroupInfo() { DLToast.showLoading() GroupService.groupInfoByKey(viewModel.groupKey).subscribe { response in DLToast.dismiss() guard let model = response.model else { return } self.viewModel.groupModel = model self.viewModel.loadData(response.list) self.rootView.selectedMemberIsSelf = self.viewModel.isCurrentUser(id: self.viewModel.memberId) // 首次加载后请求今日驾驶数据 if !self.viewModel.memberId.isEmpty { self.requestDrivingEvents(for: Date()) } }.disposed(by: disposeBag) } // 驾驶事件 private func requestDrivingEvents(start_time: String, end_time: String) { DrivingService.drivingEvents(user_id: viewModel.memberId, start_time: start_time, end_time: end_time).subscribe { response in guard let model = response.model else { return } self.rootView.mileageLab.text = String(format: "%.1fkm", model.distance_km) self.rootView.speedLab.text = String(format: "%.0fkm/h", model.max_speed) self.viewModel.updateDrivingStats(model) }.disposed(by: disposeBag) } // 行程记录 private func requestPlayback(date: String) { let start = CFAbsoluteTimeGetCurrent() dl.showLoading(text: "行程记录获取中...") DrivingService.playback(user_id: viewModel.memberId, date: date).subscribe(onNext: { [weak self] response in let elapsed = CFAbsoluteTimeGetCurrent() - start print("[playback] total: \(String(format: "%.1f", elapsed))s, items: \(response.list.count)") self?.dl.dismiss() self?.viewModel.loadScheduleRecords(response.list) }, onError: { _ in self.dl.dismiss() }).disposed(by: disposeBag) } // MARK: - Init init(groupKey: String) { viewModel = GroupMemberListVM(groupKey: groupKey) super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } }