Files
suixinkan_uikit/suixinkan/UI/LocationReport/LocationReportHistoryViewController.swift
汉秋 6336feff27 完善景区排队设置页与全局按钮配置迁移。
重构排队设置与变更日志交互,补充 Overlay 与资源,并将多页面主操作按钮统一到 UIButton Configuration,同步更新相关单元测试。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-14 16:21:53 +08:00

185 lines
7.2 KiB
Swift
Raw Permalink 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.

//
// LocationReportHistoryViewController.swift
// suixinkan
//
import SnapKit
import UIKit
/// Android `LocationReportHistoryScreen`
final class LocationReportHistoryViewController: BaseViewController, UITableViewDelegate, UITableViewDataSource {
private let viewModel: LocationReportHistoryViewModel
private let homeAPI: HomeAPI
private let filterBar = LocationReportHistoryFilterBar()
private let tableView = UITableView(frame: .zero, style: .plain)
private let emptyLabel = UILabel()
private let footerLabel = UILabel()
private let refreshControl = UIRefreshControl()
init(
viewModel: LocationReportHistoryViewModel = LocationReportHistoryViewModel(),
homeAPI: HomeAPI? = nil
) {
self.viewModel = viewModel
self.homeAPI = homeAPI ?? NetworkServices.shared.homeAPI
super.init(nibName: nil, bundle: nil)
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func setupNavigationBar() {
title = "位置上报历史"
}
override func setupUI() {
tableView.delegate = self
tableView.dataSource = self
tableView.separatorStyle = .none
tableView.backgroundColor = AppColor.pageBackground
tableView.register(LocationReportHistoryCell.self, forCellReuseIdentifier: LocationReportHistoryCell.reuseIdentifier)
tableView.refreshControl = refreshControl
emptyLabel.text = "暂无位置上报记录"
emptyLabel.textColor = AppColor.textTertiary
emptyLabel.textAlignment = .center
emptyLabel.isHidden = true
footerLabel.text = "没有更多"
footerLabel.font = .systemFont(ofSize: 13)
footerLabel.textColor = AppColor.textTertiary
footerLabel.textAlignment = .center
footerLabel.isHidden = true
view.addSubview(filterBar)
view.addSubview(tableView)
view.addSubview(emptyLabel)
view.addSubview(footerLabel)
filterBar.snp.makeConstraints { make in
make.top.leading.trailing.equalTo(view.safeAreaLayoutGuide)
}
tableView.snp.makeConstraints { make in
make.top.equalTo(filterBar.snp.bottom)
make.leading.trailing.bottom.equalTo(view.safeAreaLayoutGuide)
}
emptyLabel.snp.makeConstraints { make in
make.center.equalTo(tableView)
}
footerLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.bottom.equalTo(view.safeAreaLayoutGuide).offset(-12)
}
}
override func bindActions() {
viewModel.onStateChange = { [weak self] in
Task { @MainActor in self?.applyViewModel() }
}
viewModel.onShowMessage = { [weak self] message in
Task { @MainActor in self?.showToast(message) }
}
refreshControl.addTarget(self, action: #selector(refreshTapped), for: .valueChanged)
filterBar.onFilterTap = { [weak self] in self?.presentFilterPicker() }
filterBar.onDateFilterTap = { [weak self] in self?.presentDateFilter() }
filterBar.onClearDates = { [weak self] in
guard let self else { return }
Task { await self.viewModel.clearDateRange(api: self.homeAPI) }
}
Task { await viewModel.loadReportList(api: homeAPI, refresh: true) }
}
private func applyViewModel() {
filterBar.apply(
filterTitle: viewModel.selectedFilter.displayName,
startText: viewModel.startDateText,
endText: viewModel.endDateText,
showsDateRange: viewModel.hasDateFilter
)
tableView.reloadData()
emptyLabel.isHidden = !viewModel.reportList.isEmpty
footerLabel.isHidden = viewModel.reportList.isEmpty || viewModel.canLoadMore
if !viewModel.isRefreshing {
refreshControl.endRefreshing()
}
}
@objc private func refreshTapped() {
Task { await viewModel.loadReportList(api: homeAPI, refresh: true) }
}
private func presentFilterPicker() {
let alert = UIAlertController(title: "筛选类型", message: nil, preferredStyle: .actionSheet)
LocationReportFilterType.allCases.forEach { filter in
alert.addAction(UIAlertAction(title: filter.displayName, style: .default) { [weak self] _ in
guard let self else { return }
Task { await self.viewModel.selectFilter(filter, api: self.homeAPI) }
})
}
alert.addAction(UIAlertAction(title: "取消", style: .cancel))
present(alert, animated: true)
}
private func presentDateFilter() {
let alert = UIAlertController(title: "时间筛选", message: nil, preferredStyle: .actionSheet)
alert.addAction(UIAlertAction(title: "选择开始日期", style: .default) { [weak self] _ in
self?.presentDatePicker(isStart: true)
})
alert.addAction(UIAlertAction(title: "选择结束日期", style: .default) { [weak self] _ in
self?.presentDatePicker(isStart: false)
})
alert.addAction(UIAlertAction(title: "取消", style: .cancel))
present(alert, animated: true)
}
private func presentDatePicker(isStart: Bool) {
let alert = UIAlertController(title: isStart ? "开始日期" : "结束日期", message: "\n\n\n\n\n\n\n\n", preferredStyle: .actionSheet)
let picker = UIDatePicker()
picker.datePickerMode = .date
if #available(iOS 13.4, *) {
picker.preferredDatePickerStyle = .wheels
}
picker.date = isStart ? (viewModel.startDate ?? Date()) : (viewModel.endDate ?? Date())
alert.view.addSubview(picker)
picker.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(24)
}
alert.addAction(UIAlertAction(title: "确定", style: .default) { [weak self] _ in
guard let self else { return }
Task {
if isStart {
await self.viewModel.setDateRange(start: picker.date, end: self.viewModel.endDate, api: self.homeAPI)
} else {
await self.viewModel.setDateRange(start: self.viewModel.startDate, end: picker.date, api: self.homeAPI)
}
}
})
alert.addAction(UIAlertAction(title: "取消", style: .cancel))
present(alert, animated: true)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
viewModel.reportList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(
withIdentifier: LocationReportHistoryCell.reuseIdentifier,
for: indexPath
) as! LocationReportHistoryCell
cell.apply(item: viewModel.reportList[indexPath.row])
return cell
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
Task { await viewModel.loadMoreIfNeeded(currentIndex: indexPath.row, api: homeAPI) }
}
}