Files
suixinkan_uikit/suixinkan/UI/CooperationOrder/CommissionRateLogViewController.swift

285 lines
11 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.

//
// CommissionRateLogViewController.swift
// suixinkan
//
import SnapKit
import UIKit
///
final class CommissionRateLogViewController: BaseViewController {
private let viewModel: CommissionRateLogViewModel
private let orderAPI: OrderAPI
private let navigationTitle: String
private let tableView = UITableView(frame: .zero, style: .plain)
private let emptyLabel = UILabel()
private let loadingIndicator = UIActivityIndicatorView(style: .medium)
private let footerLabel = UILabel()
private var dataSource: UITableViewDiffableDataSource<Int, CommissionRateLogEntity>!
init(saleUserId: Int, acquirerName: String, orderAPI: OrderAPI? = nil) {
viewModel = CommissionRateLogViewModel(saleUserId: saleUserId)
let trimmedName = acquirerName.trimmingCharacters(in: .whitespacesAndNewlines)
navigationTitle = trimmedName.isEmpty ? "修改记录" : "\(trimmedName)的分成记录"
self.orderAPI = orderAPI ?? NetworkServices.shared.orderAPI
super.init(nibName: nil, bundle: nil)
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
Task { await viewModel.refresh(api: orderAPI) }
}
override func setupNavigationBar() {
title = navigationTitle
}
override func setupUI() {
view.backgroundColor = AppColor.pageBackground
tableView.backgroundColor = AppColor.pageBackground
tableView.separatorStyle = .none
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 132
tableView.delegate = self
tableView.contentInset.top = AppSpacing.sm
tableView.register(
CommissionRateLogTimelineCell.self,
forCellReuseIdentifier: CommissionRateLogTimelineCell.reuseIdentifier
)
tableView.refreshControl = UIRefreshControl()
tableView.refreshControl?.addTarget(self, action: #selector(refreshPulled), for: .valueChanged)
dataSource = UITableViewDiffableDataSource<Int, CommissionRateLogEntity>(
tableView: tableView
) { tableView, indexPath, item in
guard let cell = tableView.dequeueReusableCell(
withIdentifier: CommissionRateLogTimelineCell.reuseIdentifier,
for: indexPath
) as? CommissionRateLogTimelineCell else {
return UITableViewCell()
}
cell.configure(with: item, isLatest: indexPath.item == 0)
return cell
}
emptyLabel.text = "暂无修改记录"
emptyLabel.font = .systemFont(ofSize: 14)
emptyLabel.textColor = AppColor.textTertiary
emptyLabel.textAlignment = .center
emptyLabel.isHidden = true
loadingIndicator.color = AppColor.primary
footerLabel.font = .systemFont(ofSize: 13)
footerLabel.textColor = AppColor.textTertiary
footerLabel.textAlignment = .center
view.addSubview(tableView)
view.addSubview(emptyLabel)
view.addSubview(loadingIndicator)
}
override func setupConstraints() {
tableView.snp.makeConstraints { make in
make.edges.equalTo(view.safeAreaLayoutGuide)
}
emptyLabel.snp.makeConstraints { make in
make.center.equalTo(tableView)
}
loadingIndicator.snp.makeConstraints { make in
make.center.equalTo(tableView)
}
}
override func bindActions() {
viewModel.onStateChange = { [weak self] in
Task { @MainActor in self?.applyState() }
}
viewModel.onShowMessage = { [weak self] message in
Task { @MainActor in self?.showToast(message) }
}
}
private func applyState() {
if !viewModel.isRefreshing {
tableView.refreshControl?.endRefreshing()
}
if viewModel.initialLoading && viewModel.items.isEmpty {
loadingIndicator.startAnimating()
} else {
loadingIndicator.stopAnimating()
}
emptyLabel.isHidden = viewModel.initialLoading || viewModel.isRefreshing || !viewModel.items.isEmpty
var snapshot = NSDiffableDataSourceSnapshot<Int, CommissionRateLogEntity>()
snapshot.appendSections([0])
snapshot.appendItems(viewModel.items)
dataSource.apply(snapshot, animatingDifferences: true)
guard !viewModel.items.isEmpty else {
tableView.tableFooterView = UIView()
return
}
footerLabel.text = viewModel.isLoadingMore ? "加载中…" : (viewModel.canLoadMore ? "" : "没有更多")
footerLabel.frame = CGRect(x: 0, y: 0, width: view.bounds.width, height: 52)
tableView.tableFooterView = footerLabel
}
@objc private func refreshPulled() {
Task { await viewModel.refresh(api: orderAPI) }
}
}
extension CommissionRateLogViewController: UITableViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard !viewModel.items.isEmpty,
scrollView.contentOffset.y > scrollView.contentSize.height - scrollView.bounds.height - 180 else {
return
}
Task { await viewModel.loadMore(api: orderAPI) }
}
}
/// Cell
final class CommissionRateLogTimelineCell: UITableViewCell {
static let reuseIdentifier = "CommissionRateLogTimelineCell"
private let railView = UIView()
private let dotView = UIView()
private let cardView = UIView()
private let metadataLabel = UILabel()
private let latestBadgeLabel = UILabel()
private let beforeRateLabel = UILabel()
private let arrowImageView = UIImageView(image: UIImage(systemName: "arrow.right"))
private let afterRateLabel = UILabel()
private let remarkLabel = UILabel()
private let contentStack = UIStackView()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
selectionStyle = .none
backgroundColor = AppColor.pageBackground
contentView.backgroundColor = AppColor.pageBackground
railView.backgroundColor = AppColor.primary.withAlphaComponent(0.16)
dotView.backgroundColor = AppColor.primary
dotView.layer.cornerRadius = 4
cardView.backgroundColor = .white
cardView.layer.cornerRadius = AppRadius.md
cardView.layer.shadowColor = UIColor.black.cgColor
cardView.layer.shadowOpacity = 0.04
cardView.layer.shadowOffset = CGSize(width: 0, height: 1)
cardView.layer.shadowRadius = 2
metadataLabel.font = .systemFont(ofSize: 12)
metadataLabel.textColor = AppColor.textSecondary
metadataLabel.numberOfLines = 2
latestBadgeLabel.text = "最新"
latestBadgeLabel.font = .systemFont(ofSize: 11, weight: .semibold)
latestBadgeLabel.textColor = AppColor.primary
latestBadgeLabel.textAlignment = .center
latestBadgeLabel.backgroundColor = AppColor.primary.withAlphaComponent(0.10)
latestBadgeLabel.layer.cornerRadius = 9
latestBadgeLabel.clipsToBounds = true
latestBadgeLabel.isHidden = true
latestBadgeLabel.setContentHuggingPriority(.required, for: .horizontal)
latestBadgeLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
latestBadgeLabel.snp.makeConstraints { make in
make.width.equalTo(38)
make.height.equalTo(18)
}
let headerStack = UIStackView(arrangedSubviews: [metadataLabel, latestBadgeLabel])
headerStack.axis = .horizontal
headerStack.alignment = .top
headerStack.spacing = AppSpacing.sm
[beforeRateLabel, afterRateLabel].forEach { label in
label.font = .systemFont(ofSize: 17, weight: .semibold)
label.textColor = AppColor.textPrimary
label.setContentCompressionResistancePriority(.required, for: .horizontal)
}
afterRateLabel.textColor = AppColor.primary
arrowImageView.tintColor = AppColor.textTertiary
arrowImageView.contentMode = .scaleAspectFit
let rateStack = UIStackView(arrangedSubviews: [beforeRateLabel, arrowImageView, afterRateLabel, UIView()])
rateStack.axis = .horizontal
rateStack.alignment = .center
rateStack.spacing = AppSpacing.sm
arrowImageView.snp.makeConstraints { make in
make.size.equalTo(16)
}
remarkLabel.font = .systemFont(ofSize: 13)
remarkLabel.textColor = AppColor.textSecondary
remarkLabel.numberOfLines = 0
contentStack.axis = .vertical
contentStack.spacing = AppSpacing.sm
contentStack.addArrangedSubview(headerStack)
contentStack.addArrangedSubview(rateStack)
contentStack.addArrangedSubview(remarkLabel)
contentView.addSubview(railView)
contentView.addSubview(dotView)
contentView.addSubview(cardView)
cardView.addSubview(contentStack)
railView.snp.makeConstraints { make in
make.leading.equalToSuperview().offset(24)
make.top.bottom.equalToSuperview()
make.width.equalTo(1.5)
}
dotView.snp.makeConstraints { make in
make.centerX.equalTo(railView)
make.top.equalToSuperview().offset(20)
make.size.equalTo(8)
}
cardView.snp.makeConstraints { make in
make.leading.equalTo(railView.snp.trailing).offset(16)
make.trailing.equalToSuperview().inset(14)
make.top.equalToSuperview().offset(4)
make.bottom.equalToSuperview().inset(8)
}
contentStack.snp.makeConstraints { make in
make.edges.equalToSuperview().inset(15)
}
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
///
func configure(with item: CommissionRateLogEntity, isLatest: Bool) {
metadataLabel.text = item.metadataLine
latestBadgeLabel.isHidden = !isLatest
beforeRateLabel.text = item.displayBeforeRate
afterRateLabel.text = item.displayAfterRate
if let remark = item.displayRemark {
remarkLabel.text = "备注:\(remark)"
remarkLabel.isHidden = false
} else {
remarkLabel.text = nil
remarkLabel.isHidden = true
}
accessibilityLabel = [
isLatest ? "最新记录" : nil,
item.metadataLine,
"\(item.displayBeforeRate)修改为\(item.displayAfterRate)",
item.displayRemark.map { "备注\($0)" },
].compactMap { $0 }.joined(separator: "")
}
}